JAL-2446 merged to spike branch
[jalview.git] / test / jalview / datamodel / AlignmentAnnotationTests.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.datamodel;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertNull;
25
26 import jalview.analysis.AlignSeq;
27 import jalview.gui.JvOptionPane;
28 import jalview.io.AppletFormatAdapter;
29 import jalview.io.FileFormat;
30
31 import org.testng.Assert;
32 import org.testng.annotations.BeforeClass;
33 import org.testng.annotations.Test;
34
35 public class AlignmentAnnotationTests
36 {
37
38   @BeforeClass(alwaysRun = true)
39   public void setUpJvOptionPane()
40   {
41     JvOptionPane.setInteractiveMode(false);
42     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
43   }
44
45   @Test(groups = { "Functional" })
46   public void testCopyConstructor()
47   {
48     SequenceI sq = new Sequence("Foo", "ARAARARARAWEAWEAWRAWEAWE");
49     createAnnotation(sq);
50     AlignmentAnnotation alc, alo = sq.getAnnotation()[0];
51     alc = new AlignmentAnnotation(alo);
52     for (String key : alo.getProperties())
53     {
54       assertEquals("Property mismatch", alo.getProperty(key),
55               alc.getProperty(key));
56     }
57   }
58
59   /**
60    * create some dummy annotation derived from the sequence
61    * 
62    * @param sq
63    */
64   public static void createAnnotation(SequenceI sq)
65   {
66     Annotation[] al = new Annotation[sq.getLength()];
67     for (int i = 0; i < al.length; i++)
68     {
69       al[i] = new Annotation(new Annotation("" + sq.getCharAt(i), "",
70               (char) 0, sq.findPosition(i)));
71     }
72     AlignmentAnnotation alan = new AlignmentAnnotation("For "
73             + sq.getName(), "Fake alignment annot", al);
74     // create a sequence mapping for the annotation vector in its current state
75     alan.createSequenceMapping(sq, sq.getStart(), false);
76     alan.setProperty("CreatedBy", "createAnnotation");
77     sq.addAlignmentAnnotation(alan);
78   }
79
80   /**
81    * use this to test annotation derived from method above as it is transferred
82    * across different sequences derived from same dataset coordinate frame
83    * 
84    * @param ala
85    */
86   public static void testAnnotTransfer(AlignmentAnnotation ala)
87   {
88     assertEquals(
89             "Failed - need annotation created by createAnnotation method",
90             ala.description, "Fake alignment annot");
91     ala.adjustForAlignment();
92     for (int p = 0; p < ala.annotations.length; p++)
93     {
94       if (ala.annotations[p] != null)
95       {
96         assertEquals("Mismatch at position " + p
97                 + " between annotation position value and sequence"
98                 + ala.annotations[p], (int) ala.annotations[p].value,
99                 ala.sequenceRef.findPosition(p));
100       }
101     }
102   }
103
104   /**
105    * Tests the liftOver method and also exercises the functions for remapping
106    * annotation across different reference sequences. Here, the test is between
107    * different dataset frames (annotation transferred by mapping between
108    * sequences)
109    */
110   @Test(groups = { "Functional" })
111   public void testLiftOver()
112   {
113     SequenceI sqFrom = new Sequence("fromLong", "QQQCDEWGH");
114     sqFrom.setStart(10);
115     sqFrom.setEnd(sqFrom.findPosition(sqFrom.getLength() - 1));
116     SequenceI sqTo = new Sequence("toShort", "RCDEW");
117     sqTo.setStart(20);
118     sqTo.setEnd(sqTo.findPosition(sqTo.getLength() - 1));
119     createAnnotation(sqTo);
120     AlignmentAnnotation origTo = sqTo.getAnnotation()[0];
121     createAnnotation(sqFrom);
122     AlignmentAnnotation origFrom = sqFrom.getAnnotation()[0];
123     AlignSeq align = AlignSeq.doGlobalNWAlignment(sqFrom, sqTo,
124             AlignSeq.PEP);
125     SequenceI alSeq1 = new Sequence(sqFrom.getName(), align.getAStr1());
126     alSeq1.setStart(sqFrom.getStart() + align.getSeq1Start() - 1);
127     alSeq1.setEnd(sqFrom.getStart() + align.getSeq1End() - 1);
128     alSeq1.setDatasetSequence(sqFrom);
129     SequenceI alSeq2 = new Sequence(sqTo.getName(), align.getAStr2());
130     alSeq2.setStart(sqTo.getStart() + align.getSeq2Start() - 1);
131     alSeq2.setEnd(sqTo.getStart() + align.getSeq2End() - 1);
132     alSeq2.setDatasetSequence(sqTo);
133     System.out.println(new AppletFormatAdapter()
134 .formatSequences(
135             FileFormat.Stockholm, new Alignment(new SequenceI[] { sqFrom,
136                 alSeq1, sqTo, alSeq2 }), true));
137
138     Mapping mp = align.getMappingFromS1(false);
139
140     AlignmentAnnotation almap1 = new AlignmentAnnotation(
141             sqTo.getAnnotation()[0]);
142     almap1.liftOver(sqFrom, mp);
143     assertEquals(almap1.sequenceRef, sqFrom);
144     alSeq1.addAlignmentAnnotation(almap1);
145     almap1.setSequenceRef(alSeq1);
146     almap1.adjustForAlignment();
147     AlignmentAnnotation almap2 = new AlignmentAnnotation(
148             sqFrom.getAnnotation()[0]);
149     almap2.liftOver(sqTo, mp);
150     assertEquals(almap2.sequenceRef, sqTo);
151
152     alSeq2.addAlignmentAnnotation(almap2);
153     almap2.setSequenceRef(alSeq2);
154     almap2.adjustForAlignment();
155
156     AlignmentI all = new Alignment(new SequenceI[] { alSeq1, alSeq2 });
157     all.addAnnotation(almap1);
158     all.addAnnotation(almap2);
159     System.out.println(new AppletFormatAdapter().formatSequences(
160             FileFormat.Stockholm,
161             all, true));
162
163     for (int p = 0; p < alSeq1.getLength(); p++)
164     {
165       Annotation orig1, trans1, orig2, trans2;
166       trans2 = almap2.annotations[p];
167       orig2 = origFrom.annotations[alSeq1.findPosition(p)
168               - sqFrom.getStart()];
169       orig1 = origTo.annotations[alSeq2.findPosition(p) - sqTo.getStart()];
170       trans1 = almap1.annotations[p];
171       if (trans1 == trans2)
172       {
173         System.out.println("Pos " + p + " mismatch");
174         continue;
175       }
176       assertEquals(
177               "Mismatch on Original From and transferred annotation on 2",
178               (orig2 != null) ? orig2.toString() : null,
179               (trans2 != null) ? trans2.toString() : null);
180       assertEquals(
181               "Mismatch on Original To and transferred annotation on 1",
182               (orig1 != null) ? orig1.toString() : null,
183               (trans1 != null) ? trans1.toString() : null);
184       String alm1 = ""
185               + (almap1.annotations.length > p ? almap1.annotations[p].displayCharacter
186                       : "Out of range");
187       String alm2 = ""
188               + (almap2.annotations.length > p ? almap2.annotations[p].displayCharacter
189                       : "Out of range");
190       assertEquals("Position " + p + " " + alm1 + " " + alm2, alm1, alm2);
191     }
192   }
193
194   @Test(groups = { "Functional" })
195   public void testAdjustForAlignment()
196   {
197     SequenceI seq = new Sequence("TestSeq", "ABCDEFG");
198     seq.createDatasetSequence();
199
200     /*
201      * Annotate positions 3/4/5 (CDE) with values 1/2/3
202      */
203     Annotation[] anns = new Annotation[] { null, null, new Annotation(1),
204         new Annotation(2), new Annotation(3) };
205     AlignmentAnnotation ann = new AlignmentAnnotation("SS",
206             "secondary structure", anns);
207     seq.addAlignmentAnnotation(ann);
208
209     /*
210      * Check annotation map before modifying aligned sequence
211      */
212     assertNull(ann.getAnnotationForPosition(1));
213     assertNull(ann.getAnnotationForPosition(2));
214     assertNull(ann.getAnnotationForPosition(6));
215     assertNull(ann.getAnnotationForPosition(7));
216     assertEquals(1, ann.getAnnotationForPosition(3).value, 0.001d);
217     assertEquals(2, ann.getAnnotationForPosition(4).value, 0.001d);
218     assertEquals(3, ann.getAnnotationForPosition(5).value, 0.001d);
219
220     /*
221      * Trim the displayed sequence to BCD and adjust annotations
222      */
223     seq.setSequence("BCD");
224     seq.setStart(2);
225     seq.setEnd(4);
226     ann.adjustForAlignment();
227
228     /*
229      * Should now have annotations for aligned positions 2, 3Q (CD) only
230      */
231     assertEquals(3, ann.annotations.length);
232     assertNull(ann.annotations[0]);
233     assertEquals(1, ann.annotations[1].value, 0.001);
234     assertEquals(2, ann.annotations[2].value, 0.001);
235   }
236
237   /**
238    * Test the method that defaults rna symbol to the one matching the preceding
239    * unmatched opening bracket (if any)
240    */
241   @Test(groups = { "Functional" })
242   public void testGetDefaultRnaHelixSymbol()
243   {
244     AlignmentAnnotation ann = new AlignmentAnnotation("SS",
245             "secondary structure", null);
246     assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
247
248     Annotation[] anns = new Annotation[20];
249     ann.annotations = anns;
250     assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
251
252     anns[1] = new Annotation("(", "S", '(', 0f);
253     assertEquals("(", ann.getDefaultRnaHelixSymbol(0));
254     assertEquals("(", ann.getDefaultRnaHelixSymbol(1));
255     assertEquals(")", ann.getDefaultRnaHelixSymbol(2));
256     assertEquals(")", ann.getDefaultRnaHelixSymbol(3));
257
258     /*
259      * .(.[.{.<.}.>.).].
260      */
261     anns[1] = new Annotation("(", "S", '(', 0f);
262     anns[3] = new Annotation("[", "S", '[', 0f);
263     anns[5] = new Annotation("{", "S", '{', 0f);
264     anns[7] = new Annotation("<", "S", '<', 0f);
265     anns[9] = new Annotation("}", "S", '}', 0f);
266     anns[11] = new Annotation(">", "S", '>', 0f);
267     anns[13] = new Annotation(")", "S", ')', 0f);
268     anns[15] = new Annotation("]", "S", ']', 0f);
269
270     String expected = "(())]]}}>>>>]]]](";
271     for (int i = 0; i < expected.length(); i++)
272     {
273       assertEquals("column " + i, String.valueOf(expected.charAt(i)),
274               ann.getDefaultRnaHelixSymbol(i));
275     }
276
277     /*
278      * .(.[.(.).{.}.<.].D.
279      */
280     anns[1] = new Annotation("(", "S", '(', 0f);
281     anns[3] = new Annotation("[", "S", '[', 0f);
282     anns[5] = new Annotation("(", "S", '(', 0f);
283     anns[7] = new Annotation(")", "S", ')', 0f);
284     anns[9] = new Annotation("{", "S", '{', 0f);
285     anns[11] = new Annotation("}", "S", '}', 0f);
286     anns[13] = new Annotation("<", "S", '>', 0f);
287     anns[15] = new Annotation("]", "S", ']', 0f);
288     anns[17] = new Annotation("D", "S", 'D', 0f);
289
290     expected = "(())]]))]]}}]]>>>>dd";
291     for (int i = 0; i < expected.length(); i++)
292     {
293       assertEquals("column " + i, String.valueOf(expected.charAt(i)),
294               ann.getDefaultRnaHelixSymbol(i));
295     }
296   }
297
298   public static Annotation newAnnotation(String ann)
299   {
300     float val = 0f;
301     try
302     {
303       val = Float.parseFloat(ann);
304     } catch (NumberFormatException q)
305     {
306     }
307     ;
308     return new Annotation(ann, ann, '\0', val);
309   }
310
311   @Test(groups = { "Functional" })
312   public void testIsQuantitative()
313   {
314     AlignmentAnnotation ann = null;
315
316     ann = new AlignmentAnnotation("an", "some an", null);
317     Assert.assertFalse(ann.isQuantitative(),
318             "Empty annotation set should not be quantitative.");
319
320     ann = new AlignmentAnnotation("an", "some an", new Annotation[] {
321         newAnnotation("4"), newAnnotation("1"), newAnnotation("1"),
322         newAnnotation("0.1"), newAnnotation("0.3") });
323     Assert.assertTrue(ann.isQuantitative(),
324             "All numbers annotation set should be quantitative.");
325
326     ann = new AlignmentAnnotation("an", "some an", new Annotation[] {
327         newAnnotation("E"), newAnnotation("E"), newAnnotation("E"),
328         newAnnotation("E"), newAnnotation("E") });
329     Assert.assertFalse(ann.isQuantitative(),
330             "All 'E' annotation set should not be quantitative.");
331
332     ann = new AlignmentAnnotation("an", "some an", new Annotation[] {
333         newAnnotation("E"), newAnnotation("1"), newAnnotation("2"),
334         newAnnotation("3"), newAnnotation("E") });
335     Assert.assertTrue(ann.isQuantitative(),
336             "Mixed 'E' annotation set should be quantitative.");
337   }
338 }