X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=test%2Fjalview%2Fio%2FStockholmFileTest.java;h=7cc5258f10c6d8a113dc116d3081d2849b06a2e6;hb=fdf1d81fa8cd498d17b959427930e3fc0bc85249;hp=035f4843d099b6c3c73a5c4f224ddbe386f0cb97;hpb=37de9310bec3501cbc6381e0c3dcb282fcaad812;p=jalview.git diff --git a/test/jalview/io/StockholmFileTest.java b/test/jalview/io/StockholmFileTest.java index 035f484..7cc5258 100644 --- a/test/jalview/io/StockholmFileTest.java +++ b/test/jalview/io/StockholmFileTest.java @@ -22,40 +22,58 @@ package jalview.io; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; +import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.gui.JvOptionPane; import java.io.File; +import java.io.IOException; +import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class StockholmFileTest { + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + static String PfamFile = "examples/PF00111_seed.stk", RfamFile = "examples/RF00031_folded.stk"; @Test(groups = { "Functional" }) public void pfamFileIO() throws Exception { - testFileIOwithFormat(new File(PfamFile), "STH", -1, 0); + testFileIOwithFormat(new File(PfamFile), FileFormat.Stockholm, -1, 0, + false, false, false); } @Test(groups = { "Functional" }) public void pfamFileDataExtraction() throws Exception { AppletFormatAdapter af = new AppletFormatAdapter(); - AlignmentI al = af.readFile(PfamFile, af.FILE, - new IdentifyFile().identify(PfamFile, af.FILE)); + AlignmentI al = af.readFile(PfamFile, DataSourceType.FILE, + new IdentifyFile().identify(PfamFile, DataSourceType.FILE)); int numpdb = 0; for (SequenceI sq : al.getSequences()) { @@ -72,7 +90,8 @@ public class StockholmFileTest @Test(groups = { "Functional" }) public void rfamFileIO() throws Exception { - testFileIOwithFormat(new File(RfamFile), "STH", 2, 1); + testFileIOwithFormat(new File(RfamFile), FileFormat.Stockholm, 2, 1, + false, false, false); } /** @@ -84,10 +103,14 @@ public class StockholmFileTest * @param ioformat * - label for IO class used to write and read back in the data from * f + * @param ignoreFeatures + * @param ignoreRowVisibility + * @param allowNullAnnotations */ - public static void testFileIOwithFormat(File f, String ioformat, - int naliannot, int nminseqann) + public static void testFileIOwithFormat(File f, FileFormatI ioformat, + int naliannot, int nminseqann, boolean ignoreFeatures, + boolean ignoreRowVisibility, boolean allowNullAnnotations) { System.out.println("Reading file: " + f); String ff = f.getPath(); @@ -95,8 +118,8 @@ public class StockholmFileTest { AppletFormatAdapter rf = new AppletFormatAdapter(); - AlignmentI al = rf.readFile(ff, AppletFormatAdapter.FILE, - new IdentifyFile().identify(ff, AppletFormatAdapter.FILE)); + AlignmentI al = rf.readFile(ff, DataSourceType.FILE, + new IdentifyFile().identify(ff, DataSourceType.FILE)); assertNotNull("Couldn't read supplied alignment data.", al); @@ -110,18 +133,19 @@ public class StockholmFileTest + outputfile + "\n< orig_groups = new HashMap(); - Map new_groups = new HashMap(); + Map orig_groups = new HashMap<>(); + Map new_groups = new HashMap<>(); if (aa_new != null && aa_original != null) { @@ -192,13 +244,18 @@ public class StockholmFileTest { if (aa_new.length > i) { - assertTrue("Different alignment annotation at position " + i, - equalss(aa_original[i], aa_new[i])); + assertEqualSecondaryStructure( + "Different alignment annotation at position " + i, + aa_original[i], aa_new[i], allowNullAnnotation); // compare graphGroup or graph properties - needed to verify JAL-1299 assertEquals("Graph type not identical.", aa_original[i].graph, aa_new[i].graph); - assertEquals("Visibility not identical.", aa_original[i].visible, + if (!ignoreRowVisibility) + { + assertEquals("Visibility not identical.", + aa_original[i].visible, aa_new[i].visible); + } assertEquals("Threshold line not identical.", aa_original[i].threshold, aa_new[i].threshold); // graphGroup may differ, but pattern should be the same @@ -235,7 +292,8 @@ public class StockholmFileTest seq_original = al.getSequencesArray(); SequenceI[] seq_new = new SequenceI[al_input.getSequencesArray().length]; seq_new = al_input.getSequencesArray(); - SequenceFeature[] sequenceFeatures_original, sequenceFeatures_new; + List sequenceFeatures_original; + List sequenceFeatures_new; AlignmentAnnotation annot_original, annot_new; // for (int i = 0; i < al.getSequencesArray().length; i++) @@ -271,23 +329,20 @@ public class StockholmFileTest && seq_new[in].getSequenceFeatures() != null) { System.out.println("There are feature!!!"); - sequenceFeatures_original = new SequenceFeature[seq_original[i] - .getSequenceFeatures().length]; sequenceFeatures_original = seq_original[i] .getSequenceFeatures(); - sequenceFeatures_new = new SequenceFeature[seq_new[in] - .getSequenceFeatures().length]; sequenceFeatures_new = seq_new[in].getSequenceFeatures(); - assertEquals("different number of features", - seq_original[i].getSequenceFeatures().length, - seq_new[in].getSequenceFeatures().length); + assertEquals("different number of features", seq_original[i] + .getSequenceFeatures().size(), seq_new[in] + .getSequenceFeatures().size()); - for (int feat = 0; feat < seq_original[i].getSequenceFeatures().length; feat++) + for (int feat = 0; feat < seq_original[i].getSequenceFeatures() + .size(); feat++) { assertEquals("Different features", - sequenceFeatures_original[feat], - sequenceFeatures_new[feat]); + sequenceFeatures_original.get(feat), + sequenceFeatures_new.get(feat)); } } // compare alignment annotation @@ -301,8 +356,9 @@ public class StockholmFileTest { annot_original = al.getSequenceAt(i).getAnnotation()[j]; annot_new = al_input.getSequenceAt(in).getAnnotation()[j]; - assertTrue("Different annotation elements", - equalss(annot_original, annot_new)); + assertEqualSecondaryStructure( + "Different annotation elements", annot_original, + annot_new, allowNullAnnotation); } } } @@ -324,39 +380,78 @@ public class StockholmFileTest } } - /* - * compare annotations + /** + * compare two annotation rows, with special support for secondary structure + * comparison. With RNA, only the value and the secondaryStructure symbols are + * compared, displayCharacter and description are ignored. Annotations where + * Annotation.isWhitespace() is true are always considered equal. + * + * @param message + * - not actually used yet.. + * @param annot_or + * - the original annotation + * @param annot_new + * - the one compared to the original annotation + * @param allowNullEquivalence + * when true, positions in alignment annotation that are null will be + * considered equal to non-null positions for which + * Annotation.isWhitespace() is true. */ - private static boolean equalss(AlignmentAnnotation annot_or, - AlignmentAnnotation annot_new) + private static void assertEqualSecondaryStructure(String message, + AlignmentAnnotation annot_or, AlignmentAnnotation annot_new, + boolean allowNullEqivalence) { + // TODO: test to cover this assert behaves correctly for all allowed + // variations of secondary structure annotation row equivalence if (annot_or.annotations.length != annot_new.annotations.length) { - System.err.println("Different lengths for annotation row elements: " + fail("Different lengths for annotation row elements: " + annot_or.annotations.length + "!=" + annot_new.annotations.length); - return false; } + boolean isRna = annot_or.isRNA(); + assertTrue("Expected " + (isRna ? " valid RNA " : " no RNA ") + + " secondary structure in the row.", + isRna == annot_new.isRNA()); for (int i = 0; i < annot_or.annotations.length; i++) { Annotation an_or = annot_or.annotations[i], an_new = annot_new.annotations[i]; if (an_or != null && an_new != null) { - if (!an_or.displayCharacter.trim().equals( - an_new.displayCharacter.trim()) - || !("" + an_or.secondaryStructure).trim().equals( - ("" + an_new.secondaryStructure).trim()) - || (an_or.description != an_new.description && !((an_or.description == null && an_new.description - .trim().length() == 0) - || (an_new.description == null && an_or.description - .trim().length() == 0) || an_or.description - .trim().equals(an_new.description.trim())))) + + if (isRna) + { + if (an_or.secondaryStructure != an_new.secondaryStructure + || ((Float.isNaN(an_or.value) != Float + .isNaN(an_new.value)) || an_or.value != an_new.value)) + { + fail("Different RNA secondary structure at column " + i + + " expected: [" + annot_or.annotations[i].toString() + + "] but got: [" + annot_new.annotations[i].toString() + + "]"); + } + } + else { - System.err.println("Annotation Element Mismatch\nElement " + i - + " in original: " + annot_or.annotations[i].toString() - + "\nElement " + i + " in new: " - + annot_new.annotations[i].toString()); - return false; + // not RNA secondary structure, so expect all elements to match... + if ((an_or.isWhitespace() != an_new.isWhitespace()) + || !an_or.displayCharacter.trim().equals( + an_new.displayCharacter.trim()) + || !("" + an_or.secondaryStructure).trim().equals( + ("" + an_new.secondaryStructure).trim()) + || (an_or.description != an_new.description && !((an_or.description == null && an_new.description + .trim().length() == 0) + || (an_new.description == null && an_or.description + .trim().length() == 0) || an_or.description + .trim().equals(an_new.description.trim()))) + || !((Float.isNaN(an_or.value) && Float + .isNaN(an_new.value)) || an_or.value == an_new.value)) + { + fail("Annotation Element Mismatch\nElement " + i + + " in original: " + annot_or.annotations[i].toString() + + "\nElement " + i + " in new: " + + annot_new.annotations[i].toString()); + } } } else if (annot_or.annotations[i] == null @@ -366,19 +461,471 @@ public class StockholmFileTest } else { - System.err.println("Annotation Element Mismatch\nElement " - + i - + " in original: " - + (annot_or.annotations[i] == null ? "is null" - : annot_or.annotations[i].toString()) - + "\nElement " - + i - + " in new: " - + (annot_new.annotations[i] == null ? "is null" - : annot_new.annotations[i].toString())); - return false; + if (allowNullEqivalence) + { + if (an_or != null && an_or.isWhitespace()) + + { + continue; + } + if (an_new != null && an_new.isWhitespace()) + { + continue; + } + } + // need also to test for null in one, non-SS annotation in other... + fail("Annotation Element Mismatch\nElement " + i + " in original: " + + (an_or == null ? "is null" : an_or.toString()) + + "\nElement " + i + " in new: " + + (an_new == null ? "is null" : an_new.toString())); + } + } + } + + /** + * @see assertEqualSecondaryStructure - test if two secondary structure + * annotations are not equal + * @param message + * @param an_orig + * @param an_new + * @param allowNullEquivalence + */ + public static void assertNotEqualSecondaryStructure(String message, + AlignmentAnnotation an_orig, AlignmentAnnotation an_new, + boolean allowNullEquivalence) + { + boolean thrown = false; + try + { + assertEqualSecondaryStructure("", an_orig, an_new, + allowNullEquivalence); + } catch (AssertionError af) + { + thrown = true; + } + if (!thrown) + { + fail("Expected difference for [" + an_orig + "] and [" + an_new + "]"); + } + } + private AlignmentAnnotation makeAnnot(Annotation ae) + { + return new AlignmentAnnotation("label", "description", new Annotation[] + { ae }); + } + + @Test(groups={"Functional"}) + public void testAnnotationEquivalence() + { + AlignmentAnnotation one = makeAnnot(new Annotation("", "", ' ', 1)); + AlignmentAnnotation anotherOne = makeAnnot(new Annotation("", "", ' ', + 1)); + AlignmentAnnotation sheet = makeAnnot(new Annotation("","",'E',0f)); + AlignmentAnnotation anotherSheet = makeAnnot(new Annotation("","",'E',0f)); + AlignmentAnnotation sheetWithLabel = makeAnnot(new Annotation("1", "", + 'E', 0f)); + AlignmentAnnotation anotherSheetWithLabel = makeAnnot(new Annotation( + "1", "", 'E', 0f)); + AlignmentAnnotation rnaNoDC = makeAnnot(new Annotation("","",'<',0f)); + AlignmentAnnotation anotherRnaNoDC = makeAnnot(new Annotation("","",'<',0f)); + AlignmentAnnotation rnaWithDC = makeAnnot(new Annotation("B", "", '<', + 0f)); + AlignmentAnnotation anotherRnaWithDC = makeAnnot(new Annotation("B", + "", '<', 0f)); + + // check self equivalence + for (boolean allowNull : new boolean[] { true, false }) + { + assertEqualSecondaryStructure("Should be equal", one, anotherOne, + allowNull); + assertEqualSecondaryStructure("Should be equal", sheet, anotherSheet, + allowNull); + assertEqualSecondaryStructure("Should be equal", sheetWithLabel, + anotherSheetWithLabel, allowNull); + assertEqualSecondaryStructure("Should be equal", rnaNoDC, + anotherRnaNoDC, allowNull); + assertEqualSecondaryStructure("Should be equal", rnaWithDC, + anotherRnaWithDC, allowNull); + // display character doesn't matter for RNA structure (for 2.10.2) + assertEqualSecondaryStructure("Should be equal", rnaWithDC, rnaNoDC, + allowNull); + assertEqualSecondaryStructure("Should be equal", rnaNoDC, rnaWithDC, + allowNull); + } + + // verify others are different + List aaSet = Arrays.asList(one, sheet, + sheetWithLabel, rnaWithDC); + for (int p = 0; p < aaSet.size(); p++) + { + for (int q = 0; q < aaSet.size(); q++) + { + if (p != q) + { + assertNotEqualSecondaryStructure("Should be different", + aaSet.get(p), aaSet.get(q), false); + } + else + { + assertEqualSecondaryStructure("Should be same", aaSet.get(p), + aaSet.get(q), false); + assertEqualSecondaryStructure("Should be same", aaSet.get(p), + aaSet.get(q), true); + assertNotEqualSecondaryStructure( + "Should be different to empty anot", aaSet.get(p), + makeAnnot(Annotation.EMPTY_ANNOTATION), false); + assertNotEqualSecondaryStructure( + "Should be different to empty annot", + makeAnnot(Annotation.EMPTY_ANNOTATION), aaSet.get(q), + true); + assertNotEqualSecondaryStructure("Should be different to null", + aaSet.get(p), makeAnnot(null), false); + assertNotEqualSecondaryStructure("Should be different to null", + makeAnnot(null), aaSet.get(q), true); + } } } - return true; + + // test null + + } + + String aliFile = ">Dm\nAAACCCUUUUACACACGGGAAAGGG"; + String annFile = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n" + + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t" + + "(|(|(|(|, .|, .|, .|, .|)|)|)|)|\t0.0\nROWPROPERTIES\t" + + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false"; + + String annFileCurlyWuss = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n" + + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t" + + "(|(|(|(||{|{||{|{||)|)|)|)||}|}|}|}|\t0.0\nROWPROPERTIES\t" + + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false"; + String annFileFullWuss = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n" + + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t" + + "(|(|(|(||{|{||[|[||)|)|)|)||}|}|]|]|\t0.0\nROWPROPERTIES\t" + + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false"; + + @Test(groups = { "Functional" }) + public void secondaryStructureForRNASequence() throws Exception + { + roundTripSSForRNA(aliFile, annFile); + } + + @Test(groups = { "Functional" }) + public void curlyWUSSsecondaryStructureForRNASequence() throws Exception + { + roundTripSSForRNA(aliFile, annFileCurlyWuss); + } + + @Test(groups = { "Functional" }) + public void fullWUSSsecondaryStructureForRNASequence() throws Exception + { + roundTripSSForRNA(aliFile, annFileFullWuss); + } + + @Test(groups = { "Functional" }) + public void detectWussBrackets() + { + for (char ch : new char[] { '{', '}', '[', ']', '(', ')', '<', '>' }) + { + Assert.assertTrue(StockholmFile.DETECT_BRACKETS.matchAt("" + ch, 0), + "Didn't recognise " + ch + " as a WUSS bracket"); + } + for (char ch : new char[] { '@', '!', 'V', 'Q', '*', ' ', '-', '.' }) + { + Assert.assertFalse(StockholmFile.DETECT_BRACKETS.matchAt("" + ch, 0), + "Shouldn't recognise " + ch + " as a WUSS bracket"); + } + } + private static void roundTripSSForRNA(String aliFile, String annFile) + throws Exception + { + AlignmentI al = new AppletFormatAdapter().readFile(aliFile, + DataSourceType.PASTE, jalview.io.FileFormat.Fasta); + AnnotationFile aaf = new AnnotationFile(); + aaf.readAnnotationFile(al, annFile, DataSourceType.PASTE); + al.getAlignmentAnnotation()[0].visible = true; + + // TODO: create a better 'save as ' pattern + StockholmFile sf = new StockholmFile(al); + + String stockholmFile = sf.print(al.getSequencesArray(), true); + + AlignmentI newAl = new AppletFormatAdapter().readFile(stockholmFile, + DataSourceType.PASTE, jalview.io.FileFormat.Stockholm); + // AlignmentUtils.showOrHideSequenceAnnotations(newAl.getViewport() + // .getAlignment(), Arrays.asList("Secondary Structure"), newAl + // .getViewport().getAlignment().getSequences(), true, true); + testAlignmentEquivalence(al, newAl, true, true, true); + + } + + @Test(groups = { "Functional" }) + public void testTypeToDescription() + { + assertEquals("Secondary Structure", + StockholmFile.typeToDescription("SS")); + assertEquals("Surface Accessibility", + StockholmFile.typeToDescription("SA")); + assertEquals("transmembrane", StockholmFile.typeToDescription("TM")); + assertEquals("Posterior Probability", + StockholmFile.typeToDescription("PP")); + assertEquals("ligand binding", StockholmFile.typeToDescription("LI")); + assertEquals("active site", StockholmFile.typeToDescription("AS")); + assertEquals("intron", StockholmFile.typeToDescription("IN")); + assertEquals("interacting residue", + StockholmFile.typeToDescription("IR")); + assertEquals("accession", StockholmFile.typeToDescription("AC")); + assertEquals("organism", StockholmFile.typeToDescription("OS")); + assertEquals("class", StockholmFile.typeToDescription("CL")); + assertEquals("description", StockholmFile.typeToDescription("DE")); + assertEquals("reference", StockholmFile.typeToDescription("DR")); + assertEquals("look", StockholmFile.typeToDescription("LO")); + assertEquals("Reference Positions", + StockholmFile.typeToDescription("RF")); + + // case-sensitive: + assertEquals("Rf", StockholmFile.typeToDescription("Rf")); + assertEquals("junk", StockholmFile.typeToDescription("junk")); + assertEquals("", StockholmFile.typeToDescription("")); + assertNull(StockholmFile.typeToDescription(null)); + } + + @Test(groups = { "Functional" }) + public void testDescriptionToType() + { + assertEquals("SS", + StockholmFile.descriptionToType("Secondary Structure")); + assertEquals("SA", + StockholmFile.descriptionToType("Surface Accessibility")); + assertEquals("TM", StockholmFile.descriptionToType("transmembrane")); + + // test is not case-sensitive: + assertEquals("SS", + StockholmFile.descriptionToType("secondary structure")); + + // test is white-space sensitive: + assertNull(StockholmFile.descriptionToType("secondary structure ")); + + assertNull(StockholmFile.descriptionToType("any old junk")); + assertNull(StockholmFile.descriptionToType("")); + assertNull(StockholmFile.descriptionToType(null)); + } + + @Test(groups = { "Functional" }) + public void testPrint() + { + SequenceI seq1 = new Sequence("seq1", "LKMF-RS-Q"); + SequenceI seq2 = new Sequence("seq2/10-15", "RRS-LIP-"); + SequenceI[] seqs = new SequenceI[] { seq1, seq2 }; + AlignmentI al = new Alignment(seqs); + + StockholmFile testee = new StockholmFile(al); + + /* + * basic output (sequences only): + * sequence ids are padded with 9 spaces more than the widest id + */ + String output = testee.print(seqs, true); + String expected = "# STOCKHOLM 1.0\n" + "seq1/1-7 LKMF-RS-Q\n" + + "seq2/10-15 RRS-LIP-\n//\n"; + assertEquals(expected, output); + + /* + * add some dbrefs + */ + seq1.addDBRef(new DBRefEntry("PFAM", "1", "PF00111")); + seq1.addDBRef(new DBRefEntry("UNIPROT", "1", "P83527")); + seq2.addDBRef(new DBRefEntry("RFAM", "1", "AY119185.1")); + seq2.addDBRef(new DBRefEntry("EMBL", "1", "AF125575")); + output = testee.print(seqs, true); + // PFAM and RFAM dbrefs should be output as AC, others as DR + expected = "# STOCKHOLM 1.0\n" + "#=GS seq1/1-7 AC PF00111\n" + + "#=GS seq1/1-7 DR UNIPROT ; P83527\n" + + "#=GS seq2/10-15 AC AY119185.1\n" + + "#=GS seq2/10-15 DR EMBL ; AF125575\n" + + "seq1/1-7 LKMF-RS-Q\n" + + "seq2/10-15 RRS-LIP-\n//\n"; + assertEquals(expected, output); + + /* + * add some sequence and alignment annotation + */ + Annotation[] anns = new Annotation[5]; + for (int i = 0; i < anns.length; i++) + { + anns[i] = new Annotation(String.valueOf((char) ('B' + i)), + "Desc " + i, + (char) ('C' + i), i + 3); + } + + // expect "secondary structure" to be output as #=GR seqid SS + // using the secondary structure character (CDEFG) not display char (BCDEF) + AlignmentAnnotation aa1 = new AlignmentAnnotation("secondary structure", + "ssdesc", anns); + aa1.sequenceRef = seq1; + seq1.addAlignmentAnnotation(aa1); + al.addAnnotation(aa1); + + // "sec structure" should not be output as no corresponding feature id + AlignmentAnnotation aa2 = new AlignmentAnnotation("sec structure", + "ssdesc", anns); + aa2.sequenceRef = seq2; + seq2.addAlignmentAnnotation(aa2); + al.addAnnotation(aa2); + + // alignment annotation for Reference Positions: output as #=GC RF + AlignmentAnnotation aa3 = new AlignmentAnnotation("reference positions", + "refpos", anns); + al.addAnnotation(aa3); + + // 'seq' annotation: output as seq_cons + AlignmentAnnotation aa4 = new AlignmentAnnotation("seq", "seqdesc", + anns); + al.addAnnotation(aa4); + + // 'intron' annotation: output as IN_cons + AlignmentAnnotation aa5 = new AlignmentAnnotation("intron", + "introndesc", anns); + al.addAnnotation(aa5); + + // 'binding site' annotation: output as binding_site + AlignmentAnnotation aa6 = new AlignmentAnnotation("binding site", + "bindingdesc", anns); + al.addAnnotation(aa6); + + // 'autocalc' annotation should not be output + AlignmentAnnotation aa7 = new AlignmentAnnotation("Consensus", + "consensusdesc", anns); + aa7.autoCalculated = true; + al.addAnnotation(aa7); + + // hidden annotation should not be output + AlignmentAnnotation aa8 = new AlignmentAnnotation("domains", + "domaindesc", anns); + aa8.visible = false; + al.addAnnotation(aa8); + + output = testee.print(seqs, true); + //@formatter:off + expected = + "# STOCKHOLM 1.0\n" + + "#=GS seq1/1-7 AC PF00111\n" + + "#=GS seq1/1-7 DR UNIPROT ; P83527\n" + + "#=GS seq2/10-15 AC AY119185.1\n" + + "#=GS seq2/10-15 DR EMBL ; AF125575\n" + + "#=GR seq1/1-7 SS CDEFG\n" + + "seq1/1-7 LKMF-RS-Q\n" + + "seq2/10-15 RRS-LIP-\n" + + "#=GC RF BCDEF\n" + "#=GC seq_cons BCDEF\n" + + "#=GC IN_cons BCDEF\n" + "#=GC binding_site BCDEF\n" + + "//\n"; + //@formatter:on + assertEquals(expected, output); + } + + @Test(groups = "Functional") + public void testOutputCharacter() + { + SequenceI seq = new Sequence("seq", "abc--def-"); + + Annotation[] ann = new Annotation[8]; + ann[1] = new Annotation("Z", "desc", 'E', 1f); + ann[2] = new Annotation("Q", "desc", ' ', 1f); + ann[4] = new Annotation("", "desc", 'E', 1f); + ann[6] = new Annotation("ZH", "desc", 'E', 1f); + + /* + * null annotation in column (not Secondary Structure annotation) + * should answer sequence character, or '-' if null sequence + */ + assertEquals('-', + StockholmFile.getAnnotationCharacter("RF", 0, ann[0], null)); + assertEquals('d', + StockholmFile.getAnnotationCharacter("RF", 5, ann[5], seq)); + assertEquals('-', + StockholmFile.getAnnotationCharacter("RF", 8, null, seq)); + + /* + * null annotation in column (SS annotation) should answer underscore + */ + assertEquals('_', + StockholmFile.getAnnotationCharacter("SS", 0, ann[0], seq)); + + /* + * SS secondary structure symbol + */ + assertEquals('E', + StockholmFile.getAnnotationCharacter("SS", 1, ann[1], seq)); + + /* + * no SS symbol, use label instead + */ + assertEquals('Q', + StockholmFile.getAnnotationCharacter("SS", 2, ann[2], seq)); + + /* + * SS with 2 character label - second character overrides SS symbol + */ + assertEquals('H', + StockholmFile.getAnnotationCharacter("SS", 6, ann[6], seq)); + + /* + * empty display character, not SS - answers '.' + */ + assertEquals('.', + StockholmFile.getAnnotationCharacter("RF", 4, ann[4], seq)); + } + + /** + * Test to verify that gaps are input/output as underscore in STO annotation + * + * @throws IOException + */ + @Test(groups = "Functional") + public void testRoundtripWithGaps() throws IOException + { + /* + * small extract from RF00031_folded.stk + */ + // @formatter:off + String stoData = + "# STOCKHOLM 1.0\n" + + "#=GR B.taurus.4 SS .._((.))_\n" + + "B.taurus.4 AC.UGCGU.\n" + + "#=GR B.taurus.5 SS ..((_._))\n" + + "B.taurus.5 ACUU.G.CG\n" + + "//\n"; + // @formatter:on + StockholmFile parser = new StockholmFile(stoData, DataSourceType.PASTE); + SequenceI[] seqs = parser.getSeqsAsArray(); + assertEquals(2, seqs.length); + + /* + * B.taurus.4 has a trailing gap + * rendered as underscore in Stockholm annotation + */ + assertEquals("AC.UGCGU.", seqs[0].getSequenceAsString()); + AlignmentAnnotation[] anns = seqs[0].getAnnotation(); + assertEquals(1, anns.length); + AlignmentAnnotation taurus4SS = anns[0]; + assertEquals(9, taurus4SS.annotations.length); + assertEquals(" .", taurus4SS.annotations[0].displayCharacter); + assertNull(taurus4SS.annotations[2]); // gapped position + assertNull(taurus4SS.annotations[8]); // gapped position + assertEquals('(', taurus4SS.annotations[3].secondaryStructure); + assertEquals("(", taurus4SS.annotations[3].displayCharacter); + assertEquals(')', taurus4SS.annotations[7].secondaryStructure); + + /* + * output as Stockholm and verify it matches the original input + * (gaps output as underscore in annotation lines) + * note: roundtrip test works with the input lines ordered as above; + * can also parse in other orders, but then input doesn't match output + */ + AlignmentFileWriterI afile = FileFormat.Stockholm + .getWriter(new Alignment(seqs)); + String output = afile.print(seqs, false); + assertEquals(stoData, output); } }