JAL-1645 source formatting and organise imports
[jalview.git] / test / jalview / analysis / AlignmentAnnotationUtilsTest.java
1 package jalview.analysis;
2
3 import static org.testng.AssertJUnit.assertEquals;
4 import static org.testng.AssertJUnit.assertFalse;
5 import static org.testng.AssertJUnit.assertTrue;
6
7 import jalview.datamodel.AlignmentAnnotation;
8 import jalview.datamodel.AlignmentI;
9 import jalview.datamodel.Annotation;
10 import jalview.datamodel.SequenceI;
11 import jalview.io.AppletFormatAdapter;
12
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.BitSet;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.testng.annotations.BeforeMethod;
22 import org.testng.annotations.Test;
23
24 public class AlignmentAnnotationUtilsTest
25 {
26   // 4 sequences x 13 positions
27   final static String EOL = "\n";
28
29   // @formatter:off
30   final static String TEST_DATA = 
31           ">FER_CAPAA Ferredoxin" + EOL +
32           "TIETHKEAELVG-" + EOL +
33           ">FER_CAPAN Ferredoxin, chloroplast precursor" + EOL +
34           "TIETHKEAELVG-" + EOL +
35           ">FER1_SOLLC Ferredoxin-1, chloroplast precursor" + EOL +
36           "TIETHKEEELTA-" + EOL + 
37           ">Q93XJ9_SOLTU Ferredoxin I precursor" + EOL +
38           "TIETHKEEELTA-" + EOL;
39   // @formatter:on
40
41   private static final int SEQ_ANN_COUNT = 12;
42
43   private AlignmentI alignment;
44
45   /**
46    * Test method that converts a (possibly null) array to a list.
47    */
48   @Test(groups = { "Functional" })
49   public void testAsList()
50   {
51     // null array
52     Collection<AlignmentAnnotation> c1 = AlignmentAnnotationUtils
53             .asList(null);
54     assertTrue(c1.isEmpty());
55
56     // empty array
57     AlignmentAnnotation[] anns = new AlignmentAnnotation[0];
58     c1 = AlignmentAnnotationUtils.asList(anns);
59     assertTrue(c1.isEmpty());
60
61     // non-empty array
62     anns = new AlignmentAnnotation[2];
63     anns[0] = new AlignmentAnnotation("label0", "desc0", 0.0f);
64     anns[1] = new AlignmentAnnotation("label1", "desc1", 1.0f);
65     c1 = AlignmentAnnotationUtils.asList(anns);
66     assertEquals(2, c1.size());
67     assertTrue(c1.contains(anns[0]));
68     assertTrue(c1.contains(anns[1]));
69   }
70
71   /**
72    * This output is not part of the test but may help make sense of it...
73    * 
74    * @param shownTypes
75    * @param hiddenTypes
76    */
77   protected void consoleDebug(Map<String, List<List<String>>> shownTypes,
78           Map<String, List<List<String>>> hiddenTypes)
79   {
80     for (String calcId : shownTypes.keySet())
81     {
82       System.out.println("Visible annotation types for calcId=" + calcId);
83       for (List<String> type : shownTypes.get(calcId))
84       {
85         System.out.println("   " + type);
86       }
87     }
88     for (String calcId : hiddenTypes.keySet())
89     {
90       System.out.println("Hidden annotation types for calcId=" + calcId);
91       for (List<String> type : hiddenTypes.get(calcId))
92       {
93         System.out.println("   " + type);
94       }
95     }
96   }
97
98   /**
99    * Add a sequence group to the alignment with the specified sequences (base 0)
100    * in it
101    * 
102    * @param i
103    * @param more
104    */
105   private List<SequenceI> selectSequences(int... selected)
106   {
107     List<SequenceI> result = new ArrayList<SequenceI>();
108     SequenceI[] seqs = alignment.getSequencesArray();
109     for (int i : selected)
110     {
111       result.add(seqs[i]);
112     }
113     return result;
114   }
115
116   /**
117    * Load the test alignment and generate annotations on it
118    * 
119    * @throws IOException
120    */
121   @BeforeMethod(alwaysRun = true)
122   public void setUp() throws IOException
123   {
124     alignment = new jalview.io.FormatAdapter().readFile(TEST_DATA,
125             AppletFormatAdapter.PASTE, "FASTA");
126
127     AlignmentAnnotation[] anns = new AlignmentAnnotation[SEQ_ANN_COUNT];
128     for (int i = 0; i < anns.length; i++)
129     {
130       /*
131        * Use the constructor for a positional annotation (with an Annotation
132        * array)
133        */
134       anns[i] = new AlignmentAnnotation("Label" + i, "Desc " + i,
135               new Annotation[] {});
136       anns[i].setCalcId("CalcId" + i);
137       anns[i].visible = true;
138       alignment.addAnnotation(anns[i]);
139     }
140   }
141
142   /**
143    * Test a mixture of show/hidden annotations in/outside selection group.
144    */
145   @Test(groups = { "Functional" })
146   public void testGetShownHiddenTypes_forSelectionGroup()
147   {
148     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
149     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
150     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
151     SequenceI[] seqs = alignment.getSequencesArray();
152
153     /*
154      * Configure annotation properties for test
155      */
156     // not in selection group (should be ignored):
157     // hidden annotation Label4 not in selection group
158     anns[4].sequenceRef = seqs[2];
159     anns[4].visible = false;
160     anns[7].sequenceRef = seqs[1];
161     anns[7].visible = true;
162
163     /*
164      * in selection group, hidden:
165      */
166     anns[2].sequenceRef = seqs[3]; // CalcId2/Label2
167     anns[2].visible = false;
168     anns[3].sequenceRef = seqs[3]; // CalcId3/Label2
169     anns[3].visible = false;
170     anns[3].label = "Label2";
171     anns[4].sequenceRef = seqs[3]; // CalcId2/Label3
172     anns[4].visible = false;
173     anns[4].label = "Label3";
174     anns[4].setCalcId("CalcId2");
175     anns[8].sequenceRef = seqs[0]; // CalcId9/Label9
176     anns[8].visible = false;
177     anns[8].label = "Label9";
178     anns[8].setCalcId("CalcId9");
179     /*
180      * in selection group, visible
181      */
182     anns[6].sequenceRef = seqs[0]; // CalcId6/Label6
183     anns[6].visible = true;
184     anns[9].sequenceRef = seqs[3]; // CalcId9/Label9
185     anns[9].visible = true;
186
187     List<SequenceI> selected = selectSequences(0, 3);
188     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
189             AlignmentAnnotationUtils.asList(anns), selected);
190
191     // check results; note CalcId9/Label9 is both hidden and shown (for
192     // different sequences) so should be in both
193     // shown: CalcId6/Label6 and CalcId9/Label9
194     assertEquals(2, shownTypes.size());
195     assertEquals(1, shownTypes.get("CalcId6").size());
196     assertEquals(1, shownTypes.get("CalcId6").get(0).size());
197     assertEquals("Label6", shownTypes.get("CalcId6").get(0).get(0));
198     assertEquals(1, shownTypes.get("CalcId9").size());
199     assertEquals(1, shownTypes.get("CalcId9").get(0).size());
200     assertEquals("Label9", shownTypes.get("CalcId9").get(0).get(0));
201
202     // hidden: CalcId2/Label2, CalcId2/Label3, CalcId3/Label2, CalcId9/Label9
203     assertEquals(3, hiddenTypes.size());
204     assertEquals(2, hiddenTypes.get("CalcId2").size());
205     assertEquals(1, hiddenTypes.get("CalcId2").get(0).size());
206     assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0));
207     assertEquals(1, hiddenTypes.get("CalcId2").get(1).size());
208     assertEquals("Label3", hiddenTypes.get("CalcId2").get(1).get(0));
209     assertEquals(1, hiddenTypes.get("CalcId3").size());
210     assertEquals(1, hiddenTypes.get("CalcId3").get(0).size());
211     assertEquals("Label2", hiddenTypes.get("CalcId3").get(0).get(0));
212     assertEquals(1, hiddenTypes.get("CalcId9").size());
213     assertEquals(1, hiddenTypes.get("CalcId9").get(0).size());
214     assertEquals("Label9", hiddenTypes.get("CalcId9").get(0).get(0));
215
216     consoleDebug(shownTypes, hiddenTypes);
217   }
218
219   /**
220    * Test case where there are 'grouped' annotations, visible and hidden, within
221    * and without the selection group.
222    */
223   @Test(groups = { "Functional" })
224   public void testGetShownHiddenTypes_withGraphGroups()
225   {
226     final int GROUP_3 = 3;
227     final int GROUP_4 = 4;
228     final int GROUP_5 = 5;
229     final int GROUP_6 = 6;
230
231     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
232     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
233     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
234     SequenceI[] seqs = alignment.getSequencesArray();
235
236     /*
237      * Annotations for selection group and graph group
238      * 
239      * Hidden annotations Label2, Label3, in (hidden) group 5
240      */
241     anns[2].sequenceRef = seqs[3];
242     anns[2].visible = false;
243     anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
244     anns[2].graphGroup = GROUP_5; // not a visible group
245     anns[3].sequenceRef = seqs[0];
246     anns[3].visible = false;
247     anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
248     anns[3].graphGroup = GROUP_5;
249     // need to ensure annotations have the same calcId as well
250     anns[3].setCalcId("CalcId2");
251     // annotations for a different hidden group generating the same group label
252     anns[10].sequenceRef = seqs[0];
253     anns[10].visible = false;
254     anns[10].graph = AlignmentAnnotation.LINE_GRAPH;
255     anns[10].graphGroup = GROUP_3;
256     anns[10].label = "Label3";
257     anns[10].setCalcId("CalcId2");
258     anns[11].sequenceRef = seqs[3];
259     anns[11].visible = false;
260     anns[11].graph = AlignmentAnnotation.LINE_GRAPH;
261     anns[11].graphGroup = GROUP_3;
262     anns[11].label = "Label2";
263     anns[11].setCalcId("CalcId2");
264
265     // annotations Label1 (hidden), Label5 (visible) in group 6 (visible)
266     anns[1].sequenceRef = seqs[3];
267     // being in a visible group should take precedence over this visibility
268     anns[1].visible = false;
269     anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
270     anns[1].graphGroup = GROUP_6;
271     anns[5].sequenceRef = seqs[0];
272     anns[5].visible = true;
273     anns[5].graph = AlignmentAnnotation.LINE_GRAPH;
274     anns[5].graphGroup = GROUP_6;
275     anns[5].setCalcId("CalcId1");
276     /*
277      * Annotations 0 and 4 are visible, for a different CalcId and graph group.
278      * They produce the same label as annotations 1 and 5, which should not be
279      * duplicated in the results. This case corresponds to (e.g.) many
280      * occurrences of an IUPred Short/Long annotation group, one per sequence.
281      */
282     anns[4].sequenceRef = seqs[0];
283     anns[4].visible = false;
284     anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
285     anns[4].graphGroup = GROUP_4;
286     anns[4].label = "Label1";
287     anns[4].setCalcId("CalcId1");
288     anns[0].sequenceRef = seqs[0];
289     anns[0].visible = true;
290     anns[0].graph = AlignmentAnnotation.LINE_GRAPH;
291     anns[0].graphGroup = GROUP_4;
292     anns[0].label = "Label5";
293     anns[0].setCalcId("CalcId1");
294
295     /*
296      * Annotations outwith selection group - should be ignored.
297      */
298     // Hidden grouped annotations
299     anns[6].sequenceRef = seqs[2];
300     anns[6].visible = false;
301     anns[6].graph = AlignmentAnnotation.LINE_GRAPH;
302     anns[6].graphGroup = GROUP_4;
303     anns[8].sequenceRef = seqs[1];
304     anns[8].visible = false;
305     anns[8].graph = AlignmentAnnotation.LINE_GRAPH;
306     anns[8].graphGroup = GROUP_4;
307
308     // visible grouped annotations Label7, Label9
309     anns[7].sequenceRef = seqs[2];
310     anns[7].visible = true;
311     anns[7].graph = AlignmentAnnotation.LINE_GRAPH;
312     anns[7].graphGroup = GROUP_4;
313     anns[9].sequenceRef = seqs[1];
314     anns[9].visible = true;
315     anns[9].graph = AlignmentAnnotation.LINE_GRAPH;
316     anns[9].graphGroup = GROUP_4;
317
318     /*
319      * Generate annotations[] arrays to match aligned columns
320      */
321     // adjustForAlignment(anns);
322
323     List<SequenceI> selected = selectSequences(0, 3);
324     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
325             AlignmentAnnotationUtils.asList(anns), selected);
326
327     consoleDebug(shownTypes, hiddenTypes);
328
329     // CalcId1 / Label1, Label5 (only) should be 'shown', once, as a compound
330     // type
331     assertEquals(1, shownTypes.size());
332     assertEquals(1, shownTypes.get("CalcId1").size());
333     assertEquals(2, shownTypes.get("CalcId1").get(0).size());
334     assertEquals("Label1", shownTypes.get("CalcId1").get(0).get(0));
335     assertEquals("Label5", shownTypes.get("CalcId1").get(0).get(1));
336
337     // CalcId2 / Label2, Label3 (only) should be 'hidden'
338     assertEquals(1, hiddenTypes.size());
339     assertEquals(1, hiddenTypes.get("CalcId2").size());
340     assertEquals(2, hiddenTypes.get("CalcId2").get(0).size());
341     assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0));
342     assertEquals("Label3", hiddenTypes.get("CalcId2").get(0).get(1));
343   }
344
345   /**
346    * Test method that determines visible graph groups.
347    */
348   @Test(groups = { "Functional" })
349   public void testGetVisibleGraphGroups()
350   {
351     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
352     /*
353      * a bar graph group is not included
354      */
355     anns[0].graph = AlignmentAnnotation.BAR_GRAPH;
356     anns[0].graphGroup = 1;
357     anns[0].visible = true;
358
359     /*
360      * a line graph group is included as long as one of its members is visible
361      */
362     anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
363     anns[1].graphGroup = 5;
364     anns[1].visible = false;
365     anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
366     anns[2].graphGroup = 5;
367     anns[2].visible = true;
368
369     /*
370      * a line graph group with no visible rows is not included
371      */
372     anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
373     anns[3].graphGroup = 3;
374     anns[3].visible = false;
375
376     // a visible line graph with no graph group is not included
377     anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
378     anns[4].graphGroup = -1;
379     anns[4].visible = true;
380
381     BitSet result = AlignmentAnnotationUtils
382             .getVisibleLineGraphGroups(AlignmentAnnotationUtils
383                     .asList(anns));
384     assertTrue(result.get(5));
385     assertFalse(result.get(0));
386     assertFalse(result.get(1));
387     assertFalse(result.get(2));
388     assertFalse(result.get(3));
389   }
390
391   /**
392    * Test for case where no sequence is selected. Shouldn't normally arise but
393    * check it handles it gracefully.
394    */
395   @Test(groups = { "Functional" })
396   public void testGetShownHiddenTypes_noSequenceSelected()
397   {
398     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
399     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
400     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
401     // selected sequences null
402     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
403             AlignmentAnnotationUtils.asList(anns), null);
404     assertTrue(shownTypes.isEmpty());
405     assertTrue(hiddenTypes.isEmpty());
406
407     List<SequenceI> sequences = new ArrayList<SequenceI>();
408     // selected sequences empty list
409     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
410             AlignmentAnnotationUtils.asList(anns), sequences);
411     assertTrue(shownTypes.isEmpty());
412     assertTrue(hiddenTypes.isEmpty());
413   }
414 }