JAL-1641 Further refactoring of JSON export option, introduction of Viewport to the...
[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 = 12;
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_3 = 3;
216     final int GROUP_4 = 4;
217     final int GROUP_5 = 5;
218     final int GROUP_6 = 6;
219   
220     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
221     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
222     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
223     SequenceI[] seqs = alignment.getSequencesArray();
224   
225     /*
226      * Annotations for selection group and graph group
227      * 
228      * Hidden annotations Label2, Label3, in (hidden) group 5
229      */
230     anns[2].sequenceRef = seqs[3];
231     anns[2].visible = false;
232     anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
233     anns[2].graphGroup = GROUP_5; // not a visible group
234     anns[3].sequenceRef = seqs[0];
235     anns[3].visible = false;
236     anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
237     anns[3].graphGroup = GROUP_5;
238     // need to ensure annotations have the same calcId as well
239     anns[3].setCalcId("CalcId2");
240     // annotations for a different hidden group generating the same group label
241     anns[10].sequenceRef = seqs[0];
242     anns[10].visible = false;
243     anns[10].graph = AlignmentAnnotation.LINE_GRAPH;
244     anns[10].graphGroup = GROUP_3;
245     anns[10].label = "Label3";
246     anns[10].setCalcId("CalcId2");
247     anns[11].sequenceRef = seqs[3];
248     anns[11].visible = false;
249     anns[11].graph = AlignmentAnnotation.LINE_GRAPH;
250     anns[11].graphGroup = GROUP_3;
251     anns[11].label = "Label2";
252     anns[11].setCalcId("CalcId2");
253   
254     // annotations Label1 (hidden), Label5 (visible) in group 6 (visible)
255     anns[1].sequenceRef = seqs[3];
256     // being in a visible group should take precedence over this visibility
257     anns[1].visible = false;
258     anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
259     anns[1].graphGroup = GROUP_6;
260     anns[5].sequenceRef = seqs[0];
261     anns[5].visible = true;
262     anns[5].graph = AlignmentAnnotation.LINE_GRAPH;
263     anns[5].graphGroup = GROUP_6;
264     anns[5].setCalcId("CalcId1");
265     /*
266      * Annotations 0 and 4 are visible, for a different CalcId and graph group.
267      * They produce the same label as annotations 1 and 5, which should not be
268      * duplicated in the results. This case corresponds to (e.g.) many
269      * occurrences of an IUPred Short/Long annotation group, one per sequence.
270      */
271     anns[4].sequenceRef = seqs[0];
272     anns[4].visible = false;
273     anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
274     anns[4].graphGroup = GROUP_4;
275     anns[4].label = "Label1";
276     anns[4].setCalcId("CalcId1");
277     anns[0].sequenceRef = seqs[0];
278     anns[0].visible = true;
279     anns[0].graph = AlignmentAnnotation.LINE_GRAPH;
280     anns[0].graphGroup = GROUP_4;
281     anns[0].label = "Label5";
282     anns[0].setCalcId("CalcId1");
283   
284     /*
285      * Annotations outwith selection group - should be ignored.
286      */
287     // Hidden grouped annotations
288     anns[6].sequenceRef = seqs[2];
289     anns[6].visible = false;
290     anns[6].graph = AlignmentAnnotation.LINE_GRAPH;
291     anns[6].graphGroup = GROUP_4;
292     anns[8].sequenceRef = seqs[1];
293     anns[8].visible = false;
294     anns[8].graph = AlignmentAnnotation.LINE_GRAPH;
295     anns[8].graphGroup = GROUP_4;
296
297     // visible grouped annotations Label7, Label9
298     anns[7].sequenceRef = seqs[2];
299     anns[7].visible = true;
300     anns[7].graph = AlignmentAnnotation.LINE_GRAPH;
301     anns[7].graphGroup = GROUP_4;
302     anns[9].sequenceRef = seqs[1];
303     anns[9].visible = true;
304     anns[9].graph = AlignmentAnnotation.LINE_GRAPH;
305     anns[9].graphGroup = GROUP_4;
306   
307     List<SequenceI> selected = selectSequences(0, 3);
308     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
309             AlignmentAnnotationUtils.asList(anns),
310             selected);
311   
312     consoleDebug(shownTypes, hiddenTypes);
313   
314     // CalcId1 / Label1, Label5 (only) should be 'shown', once, as a compound
315     // type
316     assertEquals(1, shownTypes.size());
317     assertEquals(1, shownTypes.get("CalcId1").size());
318     assertEquals(2, shownTypes.get("CalcId1").get(0).size());
319     assertEquals("Label1", shownTypes.get("CalcId1").get(0).get(0));
320     assertEquals("Label5", shownTypes.get("CalcId1").get(0).get(1));
321   
322     // CalcId2 / Label2, Label3 (only) should be 'hidden'
323     assertEquals(1, hiddenTypes.size());
324     assertEquals(1, hiddenTypes.get("CalcId2").size());
325     assertEquals(2, hiddenTypes.get("CalcId2").get(0).size());
326     assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0));
327     assertEquals("Label3", hiddenTypes.get("CalcId2").get(0).get(1));
328   }
329
330   /**
331    * Test method that determines visible graph groups.
332    */
333   @Test
334   public void testGetVisibleGraphGroups()
335   {
336     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
337     /*
338      * a bar graph group is not included
339      */
340     anns[0].graph = AlignmentAnnotation.BAR_GRAPH;
341     anns[0].graphGroup = 1;
342     anns[0].visible = true;
343   
344     /*
345      * a line graph group is included as long as one of its members is visible
346      */
347     anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
348     anns[1].graphGroup = 5;
349     anns[1].visible = false;
350     anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
351     anns[2].graphGroup = 5;
352     anns[2].visible = true;
353   
354     /*
355      * a line graph group with no visible rows is not included
356      */
357     anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
358     anns[3].graphGroup = 3;
359     anns[3].visible = false;
360   
361     // a visible line graph with no graph group is not included
362     anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
363     anns[4].graphGroup = -1;
364     anns[4].visible = true;
365   
366     BitSet result = AlignmentAnnotationUtils
367             .getVisibleLineGraphGroups(AlignmentAnnotationUtils
368                     .asList(anns));
369     assertTrue(result.get(5));
370     assertFalse(result.get(0));
371     assertFalse(result.get(1));
372     assertFalse(result.get(2));
373     assertFalse(result.get(3));
374   }
375
376   /**
377    * Test for case where no sequence is selected. Shouldn't normally arise but
378    * check it handles it gracefully.
379    */
380   @Test
381   public void testGetShownHiddenTypes_noSequenceSelected()
382   {
383     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
384     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
385     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
386     // selected sequences null
387     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
388             AlignmentAnnotationUtils.asList(anns), null);
389     assertTrue(shownTypes.isEmpty());
390     assertTrue(hiddenTypes.isEmpty());
391
392     List<SequenceI> sequences = new ArrayList<SequenceI>();
393     // selected sequences empty list
394     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
395             AlignmentAnnotationUtils.asList(anns), sequences);
396     assertTrue(shownTypes.isEmpty());
397     assertTrue(hiddenTypes.isEmpty());
398   }
399 }