19a725ecfb1fd3ddc09edc3e627b085dea18ad0a
[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.Assert.assertNull;
24 import static org.testng.AssertJUnit.assertEquals;
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
339   @Test(groups = "Functional")
340   public void testMakeVisibleAnnotation()
341   {
342     HiddenColumns h = new HiddenColumns();
343     Annotation[] anns = new Annotation[] { null, null, new Annotation(1),
344         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
345         new Annotation(5), new Annotation(6), new Annotation(7),
346         new Annotation(8) };
347     AlignmentAnnotation ann = new AlignmentAnnotation("an", "some an",
348             anns);
349
350     // null annotations
351     AlignmentAnnotation emptyann = new AlignmentAnnotation("an", "some ann",
352             null);
353     emptyann.makeVisibleAnnotation(h);
354     assertNull(emptyann.annotations);
355
356     emptyann.makeVisibleAnnotation(3, 4, h);
357     assertNull(emptyann.annotations);
358
359     // without bounds, does everything
360     ann.makeVisibleAnnotation(h);
361     assertEquals(12, ann.annotations.length);
362     assertNull(ann.annotations[0]);
363     assertNull(ann.annotations[1]);
364     assertEquals(1.0f, ann.annotations[2].value);
365     assertEquals(2.0f, ann.annotations[3].value);
366     assertEquals(3.0f, ann.annotations[4].value);
367     assertNull(ann.annotations[5]);
368     assertNull(ann.annotations[6]);
369     assertEquals(4.0f, ann.annotations[7].value);
370     assertEquals(5.0f, ann.annotations[8].value);
371     assertEquals(6.0f, ann.annotations[9].value);
372     assertEquals(7.0f, ann.annotations[10].value);
373     assertEquals(8.0f, ann.annotations[11].value);
374
375     // without hidden cols, just truncates
376     ann.makeVisibleAnnotation(3, 5, h);
377     assertEquals(3, ann.annotations.length);
378     assertEquals(2.0f, ann.annotations[0].value);
379     assertEquals(3.0f, ann.annotations[1].value);
380     assertNull(ann.annotations[2]);
381
382     anns = new Annotation[] { null, null, new Annotation(1),
383         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
384         new Annotation(5), new Annotation(6), new Annotation(7),
385         new Annotation(8) };
386     ann = new AlignmentAnnotation("an", "some an", anns);
387     h.hideColumns(4, 7);
388     ann.makeVisibleAnnotation(1, 9, h);
389     assertEquals(5, ann.annotations.length);
390     assertNull(ann.annotations[0]);
391     assertEquals(1.0f, ann.annotations[1].value);
392     assertEquals(2.0f, ann.annotations[2].value);
393     assertEquals(5.0f, ann.annotations[3].value);
394     assertEquals(6.0f, ann.annotations[4].value);
395
396     anns = new Annotation[] { null, null, new Annotation(1),
397         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
398         new Annotation(5), new Annotation(6), new Annotation(7),
399         new Annotation(8) };
400     ann = new AlignmentAnnotation("an", "some an", anns);
401     h.hideColumns(1, 2);
402     ann.makeVisibleAnnotation(1, 9, h);
403     assertEquals(3, ann.annotations.length);
404     assertEquals(2.0f, ann.annotations[0].value);
405     assertEquals(5.0f, ann.annotations[1].value);
406     assertEquals(6.0f, ann.annotations[2].value);
407
408     anns = new Annotation[] { null, null, new Annotation(1),
409         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
410         new Annotation(5), new Annotation(6), new Annotation(7),
411         new Annotation(8), new Annotation(9), new Annotation(10),
412         new Annotation(11), new Annotation(12), new Annotation(13),
413         new Annotation(14), new Annotation(15) };
414     ann = new AlignmentAnnotation("an", "some an", anns);
415     h = new HiddenColumns();
416     h.hideColumns(5, 18);
417     h.hideColumns(20, 21);
418     ann.makeVisibleAnnotation(1, 21, h);
419     assertEquals(5, ann.annotations.length);
420     assertEquals(1.0f, ann.annotations[1].value);
421     assertEquals(2.0f, ann.annotations[2].value);
422     assertEquals(3.0f, ann.annotations[3].value);
423     assertNull(ann.annotations[0]);
424     assertNull(ann.annotations[4]);
425   }
426 }