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