From 479307d1043e937fbace7a1375b2e94aefcbc1fa Mon Sep 17 00:00:00 2001 From: gmungoc Date: Fri, 13 Mar 2015 17:03:39 +0000 Subject: [PATCH] JAL-845 refinements + tests for making mappings between alignments --- src/jalview/analysis/AlignmentUtils.java | 115 ++++--- src/jalview/analysis/CrossRef.java | 43 +++ src/jalview/analysis/CrossRefTest.java | 72 ++++ src/jalview/appletgui/CutAndPasteTransfer.java | 5 +- src/jalview/appletgui/SplitFrame.java | 5 +- src/jalview/datamodel/Alignment.java | 25 -- src/jalview/datamodel/AlignmentI.java | 9 - test/jalview/analysis/AlignmentUtilsTests.java | 435 ++++++++++++++---------- test/jalview/datamodel/DBRefEntryTest.java | 44 +++ 9 files changed, 498 insertions(+), 255 deletions(-) create mode 100644 src/jalview/analysis/CrossRefTest.java create mode 100644 test/jalview/datamodel/DBRefEntryTest.java diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 15b3fe4..988f2d1 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -34,6 +34,7 @@ import jalview.util.MapList; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -53,15 +54,6 @@ public class AlignmentUtils { /** - * Represents the 3 possible results of trying to map one alignment to - * another. - */ - public enum MappingResult - { - Mapped, NotMapped, AlreadyMapped - } - - /** * given an existing alignment, create a new alignment including all, or up to * flankSize additional symbols from each sequence's dataset sequence * @@ -215,43 +207,91 @@ public class AlignmentUtils /** * Build mapping of protein to cDNA alignment. Mappings are made between * sequences where the cDNA translates to the protein sequence. Any new - * mappings are added to the protein alignment. Has a 3-valued result: either - * Mapped (at least one sequence mapping was created), AlreadyMapped (all - * possible sequence mappings already exist), or NotMapped (no possible - * sequence mappings exist). + * mappings are added to the protein alignment. Returns true if any mappings + * either already exist or were added, else false. * * @param proteinAlignment * @param cdnaAlignment * @return */ - public static MappingResult mapProteinToCdna( + public static boolean mapProteinToCdna( final AlignmentI proteinAlignment, final AlignmentI cdnaAlignment) { if (proteinAlignment == null || cdnaAlignment == null) { - return MappingResult.NotMapped; + return false; } - boolean mappingPossible = false; - boolean mappingPerformed = false; + Set mappedDna = new HashSet(); + Set mappedProtein = new HashSet(); - List mapped = new ArrayList(); + /* + * First pass - map sequences where cross-references exist. This include + * 1-to-many mappings to support, for example, variant cDNA. + */ + boolean mappingPerformed = mapProteinToCdna(proteinAlignment, + cdnaAlignment, mappedDna, mappedProtein, true); + /* + * Second pass - map sequences where no cross-references exist. This only + * does 1-to-1 mappings and assumes corresponding sequences are in the same + * order in the alignments. + */ + mappingPerformed |= mapProteinToCdna(proteinAlignment, cdnaAlignment, + mappedDna, mappedProtein, false); + return mappingPerformed; + } + + /** + * Make mappings between compatible sequences (where the cDNA translation + * matches the protein). + * + * @param proteinAlignment + * @param cdnaAlignment + * @param mappedDna + * a set of mapped DNA sequences (to add to) + * @param mappedProtein + * a set of mapped Protein sequences (to add to) + * @param xrefsOnly + * if true, only map sequences where xrefs exist + * @return + */ + protected static boolean mapProteinToCdna( + final AlignmentI proteinAlignment, + final AlignmentI cdnaAlignment, Set mappedDna, + Set mappedProtein, boolean xrefsOnly) + { + boolean mappingPerformed = false; List thisSeqs = proteinAlignment.getSequences(); - for (SequenceI aaSeq : thisSeqs) { + boolean proteinMapped = false; AlignedCodonFrame acf = new AlignedCodonFrame(); for (SequenceI cdnaSeq : cdnaAlignment.getSequences()) { /* - * Heuristic rule: don't map more than one AA sequence to the same cDNA; - * map progressively assuming that alignments have mappable sequences in - * the same respective order + * Always try to map if sequences have xref to each other; this supports + * variant cDNA or alternative splicing for a protein sequence. + * + * If no xrefs, try to map progressively, assuming that alignments have + * mappable sequences in corresponding order. These are not + * many-to-many, as that would risk mixing species with similar cDNA + * sequences. */ - if (mapped.contains(cdnaSeq)) + if (xrefsOnly && !CrossRef.haveCrossRef(aaSeq, cdnaSeq)) + { + continue; + } + + /* + * Don't map non-xrefd sequences more than once each. This heuristic + * allows us to pair up similar sequences in ordered alignments. + */ + if (!xrefsOnly + && (mappedProtein.contains(aaSeq) || mappedDna + .contains(cdnaSeq))) { continue; } @@ -263,31 +303,18 @@ public class AlignmentUtils { acf.addMap(cdnaSeq, aaSeq, map); mappingPerformed = true; - mapped.add(cdnaSeq); - - /* - * Heuristic rule #2: don't map AA sequence to more than one cDNA - */ - break; + proteinMapped = true; + mappedDna.add(cdnaSeq); + mappedProtein.add(aaSeq); } } } - proteinAlignment.addCodonFrame(acf); - } - - /* - * If at least one mapping was possible but none was done, then the - * alignments are already as mapped as they can be. - */ - if (mappingPossible && !mappingPerformed) - { - return MappingResult.AlreadyMapped; - } - else - { - return mappingPerformed ? MappingResult.Mapped - : MappingResult.NotMapped; + if (proteinMapped) + { + proteinAlignment.addCodonFrame(acf); + } } + return mappingPerformed; } /** diff --git a/src/jalview/analysis/CrossRef.java b/src/jalview/analysis/CrossRef.java index 47bd7bc..eed2d7e 100644 --- a/src/jalview/analysis/CrossRef.java +++ b/src/jalview/analysis/CrossRef.java @@ -583,6 +583,49 @@ public class CrossRef } /** + * Returns true if either sequence has a cross-reference to the other + * + * @param seq1 + * @param seq2 + * @return + */ + public static boolean haveCrossRef(SequenceI seq1, SequenceI seq2) + { + return hasCrossRef(seq1, seq2) || hasCrossRef(seq2, seq1); + } + + /** + * Returns true if seq1 has a cross-reference to seq2. Currently this assumes + * that sequence name is structured as Source|AccessId. + * + * @param seq1 + * @param seq2 + * @return + */ + public static boolean hasCrossRef(SequenceI seq1, SequenceI seq2) + { + if (seq1 == null || seq2 == null) + { + return false; + } + String name = seq2.getName(); + final DBRefEntry[] xrefs = seq1.getDBRef(); + if (xrefs != null) + { + for (DBRefEntry xref : xrefs) + { + String xrefName = xref.getSource() + "|" + xref.getAccessionId(); + // case-insensitive test, consistent with DBRefEntry.equalRef() + if (xrefName.equalsIgnoreCase(name)) + { + return true; + } + } + } + return false; + } + + /** * precalculate different products that can be found for seqs in dataset and * return them. * diff --git a/src/jalview/analysis/CrossRefTest.java b/src/jalview/analysis/CrossRefTest.java new file mode 100644 index 0000000..2ae4afc --- /dev/null +++ b/src/jalview/analysis/CrossRefTest.java @@ -0,0 +1,72 @@ +package jalview.analysis; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + +import org.junit.Test; + +public class CrossRefTest +{ + + /** + * Tests for the method that checks if one sequence cross-references another + */ + @Test + public void testHasCrossRef() + { + assertFalse(CrossRef.hasCrossRef(null, null)); + SequenceI seq1 = new Sequence("EMBL|A12345", "ABCDEF"); + assertFalse(CrossRef.hasCrossRef(seq1, null)); + assertFalse(CrossRef.hasCrossRef(null, seq1)); + SequenceI seq2 = new Sequence("UNIPROT|V20192", "ABCDEF"); + assertFalse(CrossRef.hasCrossRef(seq1, seq2)); + + // different ref + seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "v20193")); + assertFalse(CrossRef.hasCrossRef(seq1, seq2)); + + // case-insensitive; version number is ignored + seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "v20192")); + assertTrue(CrossRef.hasCrossRef(seq1, seq2)); + + // right case! + seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192")); + assertTrue(CrossRef.hasCrossRef(seq1, seq2)); + // test is one-way only + assertFalse(CrossRef.hasCrossRef(seq2, seq1)); + } + + /** + * Tests for the method that checks if either sequence cross-references the + * other + */ + @Test + public void testHaveCrossRef() + { + assertFalse(CrossRef.hasCrossRef(null, null)); + SequenceI seq1 = new Sequence("EMBL|A12345", "ABCDEF"); + assertFalse(CrossRef.haveCrossRef(seq1, null)); + assertFalse(CrossRef.haveCrossRef(null, seq1)); + SequenceI seq2 = new Sequence("UNIPROT|V20192", "ABCDEF"); + assertFalse(CrossRef.haveCrossRef(seq1, seq2)); + + seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192")); + assertTrue(CrossRef.haveCrossRef(seq1, seq2)); + // next is true for haveCrossRef, false for hasCrossRef + assertTrue(CrossRef.haveCrossRef(seq2, seq1)); + + // now the other way round + seq1.setDBRef(null); + seq2.addDBRef(new DBRefEntry("EMBL", "1", "A12345")); + assertTrue(CrossRef.haveCrossRef(seq1, seq2)); + assertTrue(CrossRef.haveCrossRef(seq2, seq1)); + + // now both ways + seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "V20192")); + assertTrue(CrossRef.haveCrossRef(seq1, seq2)); + assertTrue(CrossRef.haveCrossRef(seq2, seq1)); + } +} diff --git a/src/jalview/appletgui/CutAndPasteTransfer.java b/src/jalview/appletgui/CutAndPasteTransfer.java index 7bef6b2..963961d 100644 --- a/src/jalview/appletgui/CutAndPasteTransfer.java +++ b/src/jalview/appletgui/CutAndPasteTransfer.java @@ -21,7 +21,6 @@ package jalview.appletgui; import jalview.analysis.AlignmentUtils; -import jalview.analysis.AlignmentUtils.MappingResult; import jalview.bin.JalviewLite; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; @@ -270,8 +269,8 @@ public class CutAndPasteTransfer extends Panel implements ActionListener, } AlignmentI protein = thisAlignment.isNucleotide() ? al : thisAlignment; AlignmentI dna = thisAlignment.isNucleotide() ? thisAlignment : al; - MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, dna); - if (mapped == MappingResult.NotMapped) + boolean mapped = AlignmentUtils.mapProteinToCdna(protein, dna); + if (!mapped) { return false; } diff --git a/src/jalview/appletgui/SplitFrame.java b/src/jalview/appletgui/SplitFrame.java index ccfa432..cdd1605 100644 --- a/src/jalview/appletgui/SplitFrame.java +++ b/src/jalview/appletgui/SplitFrame.java @@ -1,7 +1,6 @@ package jalview.appletgui; import jalview.analysis.AlignmentUtils; -import jalview.analysis.AlignmentUtils.MappingResult; import jalview.api.ViewStyleI; import jalview.bin.JalviewLite; import jalview.datamodel.AlignmentI; @@ -54,9 +53,9 @@ public class SplitFrame extends EmbmenuFrame AlignViewport protein = !topAlignment.isNucleotide() ? topViewport : (!bottomAlignment.isNucleotide() ? bottomViewport : null); - MappingResult mapped = AlignmentUtils.mapProteinToCdna( + boolean mapped = AlignmentUtils.mapProteinToCdna( protein.getAlignment(), cdna.getAlignment()); - if (mapped != MappingResult.NotMapped) + if (mapped) { final StructureSelectionManager ssm = StructureSelectionManager .getStructureSelectionManager(topViewport.applet); diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index ad5d685..482df7f 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -1731,29 +1731,4 @@ public class Alignment implements AlignmentI } return names; } - - /** - * Returns a (possibly empty) alignment whose sequences are aligned to match - * the current alignment, as mapped by the given codon mappings. - * - * @param codonFrames - * @return - */ - @Override - public AlignmentI getAlignedComplement(Set codonFrames) - { - // Note: passing codonFrames as a parameter rather than using - // this.codonFrameList as more flexible. Specifically, mappings are held - // on the protein alignment but we might want to act on dna. - - // TODO we want the gap character of the mapped alignment, not this one! - List alignedSeqs = AlignmentUtils.getAlignedTranslation( - getSequences(), getGapCharacter(), codonFrames); - final SequenceI[] seqsAsArray = alignedSeqs - .toArray(new SequenceI[alignedSeqs.size()]); - AlignmentI al = new Alignment(seqsAsArray); - al.padGaps(); - al.setDataset(null); - return al; - } } diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index 526f362..67e5743 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -517,13 +517,4 @@ public interface AlignmentI extends AnnotatedCollectionI * @return */ Set getSequenceNames(); - - /** - * Returns a (possibly empty) alignment whose sequences are aligned to match - * the current alignment, as mapped by the given codon mappings. - * - * @param codonFrames - * @return - */ - AlignmentI getAlignedComplement(Set codonFrames); } diff --git a/test/jalview/analysis/AlignmentUtilsTests.java b/test/jalview/analysis/AlignmentUtilsTests.java index 5482cba..59a265f 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -24,10 +24,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import jalview.analysis.AlignmentUtils.MappingResult; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefEntry; import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; @@ -36,6 +36,7 @@ import jalview.io.FormatAdapter; import jalview.util.MapList; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -140,132 +141,48 @@ public class AlignmentUtilsTests a.setDataset(null); return a; } + /** - * Test mapping of protein to cDNA. + * Test mapping of protein to cDNA, for the case where we have no sequence + * cross-references, so mappings are made first-served 1-1 where sequences + * translate. * * @throws IOException */ @Test - public void testMapProteinToCdna() throws IOException + public void testMapProteinToCdna_noXrefs() throws IOException { - // protein: Human + Mouse, 3 residues - AlignmentI protein = loadAlignment( - ">Human\nKQY\n>Mouse\nAFP\n>Worm\nRST\n", - "FASTA"); - // cDNA: Mouse, Human, Mouse, 9 bases - // @formatter:off - String dnaData = - ">Mouse\nGAAATCCAG\n" + - ">Human\nTTCGATTAC\n" + - ">Mouse\nGTCGTTTGC\n" + - ">Mouse\nGTCGTTTGCgac\n" + // not mapped - wrong length - ">Fly\nGTCGTTTGC\n"; // not mapped - no name match - // @formatter:on - AlignmentI cdna1 = loadAlignment( - dnaData, - "FASTA"); - MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna1); - assertEquals(mapped, MappingResult.Mapped); - - /* - * Check two mappings (one for Mouse, one for Human) - */ - assertEquals(2, protein.getCodonFrames().size()); - assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); - assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); - - /* - * Inspect mapping for Human protein - */ - AlignedCodonFrame humanMapping = protein.getCodonFrame( - protein.getSequenceAt(0)).get(0); - assertEquals(1, humanMapping.getdnaSeqs().length); - assertEquals(cdna1.getSequenceAt(1).getDatasetSequence(), - humanMapping.getdnaSeqs()[0]); - Mapping[] protMappings = humanMapping.getProtMappings(); - assertEquals(1, protMappings.length); - MapList mapList = protMappings[0].getMap(); - assertEquals(3, mapList.getFromRatio()); - assertEquals(1, mapList.getToRatio()); - assertTrue(Arrays.equals(new int[] - { 1, 9 }, mapList.getFromRanges().get(0))); - assertEquals(1, mapList.getFromRanges().size()); - assertTrue(Arrays.equals(new int[] - { 1, 3 }, mapList.getToRanges().get(0))); - assertEquals(1, mapList.getToRanges().size()); + List protseqs = new ArrayList(); + protseqs.add(new Sequence("UNIPROT|V12345", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12346", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12347", "SAR")); + AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3])); + protein.setDataset(null); - /* - * Inspect mappings for Mouse protein - */ - AlignedCodonFrame mouseMapping1 = protein.getCodonFrame( - protein.getSequenceAt(1)).get(0); - assertEquals(2, mouseMapping1.getdnaSeqs().length); - assertEquals(cdna1.getSequenceAt(0).getDatasetSequence(), - mouseMapping1.getdnaSeqs()[0]); - assertEquals(cdna1.getSequenceAt(2).getDatasetSequence(), - mouseMapping1.getdnaSeqs()[1]); - protMappings = mouseMapping1.getProtMappings(); - assertEquals(2, protMappings.length); - for (int i = 0; i < 2; i++) - { - mapList = protMappings[i].getMap(); - assertEquals(3, mapList.getFromRatio()); - assertEquals(1, mapList.getToRatio()); - assertTrue(Arrays.equals(new int[] - { 1, 9 }, mapList.getFromRanges().get(0))); - assertEquals(1, mapList.getFromRanges().size()); - assertTrue(Arrays.equals(new int[] - { 1, 3 }, mapList.getToRanges().get(0))); - assertEquals(1, mapList.getToRanges().size()); - } - } + List dnaseqs = new ArrayList(); + dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR + dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAA")); // = EIQ + dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ + dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ + AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4])); + cdna.setDataset(null); - /** - * Test mapping of protein to cDNA which may include start and/or stop codons. - * - * @throws IOException - */ - @Test - public void testMapProteinToCdna_stopStartCodons() throws IOException - { - // protein: Human + Mouse, 3 residues - AlignmentI protein = loadAlignment( - ">Human\nKQY\n>Mouse\nAFP\n>Worm\nRST\n", "FASTA"); - // @formatter:off - String dnaData = - ">Mouse\natgGAAATCCAG\n" + // Mouse with start codon - ">Human\nTTCGATtactaa\n" + // Human with stop codon TAA - ">Mouse\nGTCGTTTGctaG\n" + // Mouse with stop codon TAG - ">Human\nGTCGTTTgctGa\n" + // Human with stop codon TGA - ">Mouse\nATGGTCGTTTGCtag\n"; // Mouse with start and stop codons - // @formatter:on - AlignmentI cdna1 = loadAlignment( - dnaData, - "FASTA"); - MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna1); - assertEquals(mapped, MappingResult.Mapped); + assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna)); - /* - * Check two mappings (one for Mouse, one for Human) - */ - assertEquals(2, protein.getCodonFrames().size()); + // 3 mappings made, each from 1 to 1 sequence + assertEquals(3, protein.getCodonFrames().size()); assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size()); - /* - * Inspect mapping for Human protein - should map to 2nd and 4th cDNA seqs - */ - AlignedCodonFrame humanMapping = protein.getCodonFrame( + // V12345 mapped to A22222 + AlignedCodonFrame acf = protein.getCodonFrame( protein.getSequenceAt(0)).get(0); - assertEquals(2, humanMapping.getdnaSeqs().length); - assertEquals(cdna1.getSequenceAt(1).getDatasetSequence(), - humanMapping.getdnaSeqs()[0]); - assertEquals(cdna1.getSequenceAt(3).getDatasetSequence(), - humanMapping.getdnaSeqs()[1]); - Mapping[] protMappings = humanMapping.getProtMappings(); - // two mappings, both to cDNA with stop codon - assertEquals(2, protMappings.length); - + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(1).getDatasetSequence(), + acf.getdnaSeqs()[0]); + Mapping[] protMappings = acf.getProtMappings(); + assertEquals(1, protMappings.length); MapList mapList = protMappings[0].getMap(); assertEquals(3, mapList.getFromRatio()); assertEquals(1, mapList.getToRatio()); @@ -276,65 +193,20 @@ public class AlignmentUtilsTests { 1, 3 }, mapList.getToRanges().get(0))); assertEquals(1, mapList.getToRanges().size()); - mapList = protMappings[1].getMap(); - assertEquals(3, mapList.getFromRatio()); - assertEquals(1, mapList.getToRatio()); - assertTrue(Arrays.equals(new int[] - { 1, 9 }, mapList.getFromRanges().get(0))); - assertEquals(1, mapList.getFromRanges().size()); - assertTrue(Arrays.equals(new int[] - { 1, 3 }, mapList.getToRanges().get(0))); - assertEquals(1, mapList.getToRanges().size()); + // V12346 mapped to A33333 + acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(2).getDatasetSequence(), + acf.getdnaSeqs()[0]); - /* - * Inspect mapping for Mouse protein - should map to 1st/3rd/5th cDNA seqs - */ - AlignedCodonFrame mouseMapping = protein.getCodonFrame( - protein.getSequenceAt(1)).get(0); - assertEquals(3, mouseMapping.getdnaSeqs().length); - assertEquals(cdna1.getSequenceAt(0).getDatasetSequence(), - mouseMapping.getdnaSeqs()[0]); - assertEquals(cdna1.getSequenceAt(2).getDatasetSequence(), - mouseMapping.getdnaSeqs()[1]); - assertEquals(cdna1.getSequenceAt(4).getDatasetSequence(), - mouseMapping.getdnaSeqs()[2]); - - // three mappings - protMappings = mouseMapping.getProtMappings(); - assertEquals(3, protMappings.length); - - // first mapping to cDNA with start codon - mapList = protMappings[0].getMap(); - assertEquals(3, mapList.getFromRatio()); - assertEquals(1, mapList.getToRatio()); - assertTrue(Arrays.equals(new int[] - { 4, 12 }, mapList.getFromRanges().get(0))); - assertEquals(1, mapList.getFromRanges().size()); - assertTrue(Arrays.equals(new int[] - { 1, 3 }, mapList.getToRanges().get(0))); - assertEquals(1, mapList.getToRanges().size()); + // V12347 mapped to A11111 + acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(0).getDatasetSequence(), + acf.getdnaSeqs()[0]); - // second mapping to cDNA with stop codon - mapList = protMappings[1].getMap(); - assertEquals(3, mapList.getFromRatio()); - assertEquals(1, mapList.getToRatio()); - assertTrue(Arrays.equals(new int[] - { 1, 9 }, mapList.getFromRanges().get(0))); - assertEquals(1, mapList.getFromRanges().size()); - assertTrue(Arrays.equals(new int[] - { 1, 3 }, mapList.getToRanges().get(0))); - assertEquals(1, mapList.getToRanges().size()); - - // third mapping to cDNA with start and stop codon - mapList = protMappings[2].getMap(); - assertEquals(3, mapList.getFromRatio()); - assertEquals(1, mapList.getToRatio()); - assertTrue(Arrays.equals(new int[] - { 4, 12 }, mapList.getFromRanges().get(0))); - assertEquals(1, mapList.getFromRanges().size()); - assertTrue(Arrays.equals(new int[] - { 1, 3 }, mapList.getToRanges().get(0))); - assertEquals(1, mapList.getToRanges().size()); + // no mapping involving the 'extra' A44444 + assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty()); } /** @@ -605,4 +477,225 @@ public class AlignmentUtilsTests 0, "FPMG".toCharArray())); } + + /** + * Test mapping of protein to cDNA, for cases where the cDNA has start and/or + * stop codons in addition to the protein coding sequence. + * + * @throws IOException + */ + @Test + public void testMapProteinToCdna_withStartAndStopCodons() + throws IOException + { + List protseqs = new ArrayList(); + protseqs.add(new Sequence("UNIPROT|V12345", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12346", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12347", "SAR")); + AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3])); + protein.setDataset(null); + + List dnaseqs = new ArrayList(); + // start + SAR: + dnaseqs.add(new Sequence("EMBL|A11111", "ATGTCAGCACGC")); + // = EIQ + stop + dnaseqs.add(new Sequence("EMBL|A22222", "GAGATACAATAA")); + // = start +EIQ + stop + dnaseqs.add(new Sequence("EMBL|A33333", "ATGGAAATCCAGTAG")); + dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); + AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[4])); + cdna.setDataset(null); + + assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna)); + + // 3 mappings made, each from 1 to 1 sequence + assertEquals(3, protein.getCodonFrames().size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size()); + + // V12345 mapped from A22222 + AlignedCodonFrame acf = protein.getCodonFrame( + protein.getSequenceAt(0)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(1).getDatasetSequence(), + acf.getdnaSeqs()[0]); + Mapping[] protMappings = acf.getProtMappings(); + assertEquals(1, protMappings.length); + MapList mapList = protMappings[0].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 1, 9 }, mapList.getFromRanges().get(0))); + assertEquals(1, mapList.getFromRanges().size()); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges().get(0))); + assertEquals(1, mapList.getToRanges().size()); + + // V12346 mapped from A33333 starting position 4 + acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(2).getDatasetSequence(), + acf.getdnaSeqs()[0]); + protMappings = acf.getProtMappings(); + assertEquals(1, protMappings.length); + mapList = protMappings[0].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 4, 12 }, mapList.getFromRanges().get(0))); + assertEquals(1, mapList.getFromRanges().size()); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges().get(0))); + assertEquals(1, mapList.getToRanges().size()); + + // V12347 mapped to A11111 starting position 4 + acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(0).getDatasetSequence(), + acf.getdnaSeqs()[0]); + protMappings = acf.getProtMappings(); + assertEquals(1, protMappings.length); + mapList = protMappings[0].getMap(); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getToRatio()); + assertTrue(Arrays.equals(new int[] + { 4, 12 }, mapList.getFromRanges().get(0))); + assertEquals(1, mapList.getFromRanges().size()); + assertTrue(Arrays.equals(new int[] + { 1, 3 }, mapList.getToRanges().get(0))); + assertEquals(1, mapList.getToRanges().size()); + + // no mapping involving the 'extra' A44444 + assertTrue(protein.getCodonFrame(cdna.getSequenceAt(3)).isEmpty()); + } + + /** + * Test mapping of protein to cDNA, for the case where we have some sequence + * cross-references. Verify that 1-to-many mappings are made where + * cross-references exist and sequences are mappable. + * + * @throws IOException + */ + @Test + public void testMapProteinToCdna_withXrefs() throws IOException + { + List protseqs = new ArrayList(); + protseqs.add(new Sequence("UNIPROT|V12345", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12346", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12347", "SAR")); + AlignmentI protein = new Alignment(protseqs.toArray(new SequenceI[3])); + protein.setDataset(null); + + List dnaseqs = new ArrayList(); + dnaseqs.add(new Sequence("EMBL|A11111", "TCAGCACGC")); // = SAR + dnaseqs.add(new Sequence("EMBL|A22222", "ATGGAGATACAA")); // = start + EIQ + dnaseqs.add(new Sequence("EMBL|A33333", "GAAATCCAG")); // = EIQ + dnaseqs.add(new Sequence("EMBL|A44444", "GAAATTCAG")); // = EIQ + dnaseqs.add(new Sequence("EMBL|A55555", "GAGATTCAG")); // = EIQ + AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[5])); + cdna.setDataset(null); + + // Xref A22222 to V12345 (should get mapped) + dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345")); + // Xref V12345 to A44444 (should get mapped) + protseqs.get(0).addDBRef(new DBRefEntry("EMBL", "1", "A44444")); + // Xref A33333 to V12347 (sequence mismatch - should not get mapped) + dnaseqs.get(2).addDBRef(new DBRefEntry("UNIPROT", "1", "V12347")); + // as V12345 is mapped to A22222 and A44444, this leaves V12346 unmapped. + // it should get paired up with the unmapped A33333 + // A11111 should be mapped to V12347 + // A55555 is spare and has no xref so is not mapped + + assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna)); + + // 4 protein mappings made for 3 proteins, 2 to V12345, 1 each to V12346/7 + assertEquals(3, protein.getCodonFrames().size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(2)).size()); + + // one mapping for each of the first 4 cDNA sequences + assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size()); + assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(2)).size()); + assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(3)).size()); + + // V12345 mapped to A22222 and A44444 + AlignedCodonFrame acf = protein.getCodonFrame( + protein.getSequenceAt(0)).get(0); + assertEquals(2, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(1).getDatasetSequence(), + acf.getdnaSeqs()[0]); + assertEquals(cdna.getSequenceAt(3).getDatasetSequence(), + acf.getdnaSeqs()[1]); + + // V12346 mapped to A33333 + acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(2).getDatasetSequence(), + acf.getdnaSeqs()[0]); + + // V12347 mapped to A11111 + acf = protein.getCodonFrame(protein.getSequenceAt(2)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(0).getDatasetSequence(), + acf.getdnaSeqs()[0]); + + // no mapping involving the 'extra' A55555 + assertTrue(protein.getCodonFrame(cdna.getSequenceAt(4)).isEmpty()); + } + + /** + * Test mapping of protein to cDNA, for the case where we have some sequence + * cross-references. Verify that once we have made an xref mapping we don't + * also map un-xrefd sequeces. + * + * @throws IOException + */ + @Test + public void testMapProteinToCdna_prioritiseXrefs() throws IOException + { + List protseqs = new ArrayList(); + protseqs.add(new Sequence("UNIPROT|V12345", "EIQ")); + protseqs.add(new Sequence("UNIPROT|V12346", "EIQ")); + AlignmentI protein = new Alignment( + protseqs.toArray(new SequenceI[protseqs.size()])); + protein.setDataset(null); + + List dnaseqs = new ArrayList(); + dnaseqs.add(new Sequence("EMBL|A11111", "GAAATCCAG")); // = EIQ + dnaseqs.add(new Sequence("EMBL|A22222", "GAAATTCAG")); // = EIQ + AlignmentI cdna = new Alignment(dnaseqs.toArray(new SequenceI[dnaseqs + .size()])); + cdna.setDataset(null); + + // Xref A22222 to V12345 (should get mapped) + // A11111 should then be mapped to the unmapped V12346 + dnaseqs.get(1).addDBRef(new DBRefEntry("UNIPROT", "1", "V12345")); + + assertTrue(AlignmentUtils.mapProteinToCdna(protein, cdna)); + + // 2 protein mappings made + assertEquals(2, protein.getCodonFrames().size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(protein.getSequenceAt(1)).size()); + + // one mapping for each of the cDNA sequences + assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(0)).size()); + assertEquals(1, protein.getCodonFrame(cdna.getSequenceAt(1)).size()); + + // V12345 mapped to A22222 + AlignedCodonFrame acf = protein.getCodonFrame(protein.getSequenceAt(0)) + .get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(1).getDatasetSequence(), + acf.getdnaSeqs()[0]); + + // V12346 mapped to A11111 + acf = protein.getCodonFrame(protein.getSequenceAt(1)).get(0); + assertEquals(1, acf.getdnaSeqs().length); + assertEquals(cdna.getSequenceAt(0).getDatasetSequence(), + acf.getdnaSeqs()[0]); + } } diff --git a/test/jalview/datamodel/DBRefEntryTest.java b/test/jalview/datamodel/DBRefEntryTest.java new file mode 100644 index 0000000..38c7f51 --- /dev/null +++ b/test/jalview/datamodel/DBRefEntryTest.java @@ -0,0 +1,44 @@ +package jalview.datamodel; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import jalview.util.MapList; + +import org.junit.Test; + +public class DBRefEntryTest +{ + + /** + * Tests for the method that compares equality of reference (but not mapping) + */ + @Test + public void testEqualRef() + { + DBRefEntry ref1 = new DBRefEntry("UNIPROT", "1", "V71633"); + assertTrue(ref1.equalRef(ref1)); + assertFalse(ref1.equalRef(null)); + + // comparison is not case sensitive + DBRefEntry ref2 = new DBRefEntry("uniprot", "1", "v71633"); + assertTrue(ref1.equalRef(ref2)); + assertTrue(ref2.equalRef(ref1)); + + // source, version and accessionid must match + assertFalse(ref1.equalRef(new DBRefEntry("UNIPRO", "1", "V71633"))); + assertFalse(ref1.equalRef(new DBRefEntry("UNIPROT", "2", "V71633"))); + assertFalse(ref1.equalRef(new DBRefEntry("UNIPROT", "1", "V71632"))); + + // presence of or differences in mappings are ignored + ref1.setMap(new Mapping(new MapList(new int[] + { 1, 3 }, new int[] + { 1, 1 }, 3, 1))); + assertTrue(ref1.equalRef(ref2)); + assertTrue(ref2.equalRef(ref1)); + ref1.setMap(new Mapping(new MapList(new int[] + { 1, 6 }, new int[] + { 1, 2 }, 3, 1))); + assertTrue(ref1.equalRef(ref2)); + assertTrue(ref2.equalRef(ref1)); + } +} -- 1.7.10.2