JAL-2429 Additional fix to ColumnSelection when hidden cols start at 0
[jalview.git] / test / jalview / datamodel / ColumnSelectionTest.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.assertFalse;
25 import static org.testng.AssertJUnit.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27 import static org.testng.AssertJUnit.fail;
28
29 import jalview.gui.JvOptionPane;
30
31 import java.util.Arrays;
32 import java.util.BitSet;
33 import java.util.Collections;
34 import java.util.ConcurrentModificationException;
35 import java.util.List;
36
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.Test;
39
40 public class ColumnSelectionTest
41 {
42
43   @BeforeClass(alwaysRun = true)
44   public void setUpJvOptionPane()
45   {
46     JvOptionPane.setInteractiveMode(false);
47     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
48   }
49
50   @Test(groups = { "Functional" })
51   public void testAddElement()
52   {
53     ColumnSelection cs = new ColumnSelection();
54     cs.addElement(2);
55     cs.addElement(5);
56     cs.addElement(3);
57     cs.addElement(5); // ignored
58     List<Integer> sel = cs.getSelected();
59     assertEquals("[2, 5, 3]", sel.toString());
60   }
61
62   /**
63    * Test the remove method - in particular to verify that remove(int i) removes
64    * the element whose value is i, _NOT_ the i'th element.
65    */
66   @Test(groups = { "Functional" })
67   public void testRemoveElement()
68   {
69     ColumnSelection cs = new ColumnSelection();
70     cs.addElement(2);
71     cs.addElement(5);
72
73     // removing elements not in the list has no effect
74     cs.removeElement(0);
75     cs.removeElement(1);
76     List<Integer> sel = cs.getSelected();
77     assertEquals(2, sel.size());
78     assertEquals(new Integer(2), sel.get(0));
79     assertEquals(new Integer(5), sel.get(1));
80
81     // removing an element in the list removes it
82     cs.removeElement(2);
83     // ...and also from the read-only view
84     assertEquals(1, sel.size());
85     sel = cs.getSelected();
86     assertEquals(1, sel.size());
87     assertEquals(new Integer(5), sel.get(0));
88   }
89
90   /**
91    * Test the method that finds the visible column position of an alignment
92    * column, allowing for hidden columns.
93    */
94   @Test(groups = { "Functional" })
95   public void testFindColumnPosition()
96   {
97     ColumnSelection cs = new ColumnSelection();
98     assertEquals(5, cs.findColumnPosition(5));
99
100     // hiding column 6 makes no difference
101     cs.hideColumns(6, 6);
102     assertEquals(5, cs.findColumnPosition(5));
103
104     // hiding column 4 moves column 5 to column 4
105     cs.hideColumns(4, 4);
106     assertEquals(4, cs.findColumnPosition(5));
107
108     // hiding column 4 moves column 4 to position 3
109     assertEquals(3, cs.findColumnPosition(4));
110
111     // hiding columns 1 and 2 moves column 5 to column 2
112     cs.hideColumns(1, 2);
113     assertEquals(2, cs.findColumnPosition(5));
114
115     // check with > 1 hidden column regions
116     // where some columns are in the hidden regions
117     ColumnSelection cs2 = new ColumnSelection();
118     cs2.hideColumns(5, 10);
119     cs2.hideColumns(20, 27);
120     cs2.hideColumns(40, 44);
121
122     // hiding columns 5-10 and 20-27 moves column 8 to column 4
123     assertEquals(4, cs2.findColumnPosition(8));
124
125     // and moves column 24 to 13
126     assertEquals(13, cs2.findColumnPosition(24));
127
128     // and moves column 28 to 14
129     assertEquals(14, cs2.findColumnPosition(28));
130
131     // and moves column 40 to 25
132     assertEquals(25, cs2.findColumnPosition(40));
133
134     // check when hidden columns start at 0 that the visible column
135     // to the right is returned for hidden cols in the region
136     ColumnSelection cs3 = new ColumnSelection();
137     cs3.hideColumns(0, 4);
138     assertEquals(5, cs3.findColumnPosition(2));
139
140   }
141
142   /**
143    * Test the code used to locate the reference sequence ruler origin
144    */
145   @Test(groups = { "Functional" })
146   public void testLocateVisibleBoundsofSequence()
147   {
148     ColumnSelection cs = new ColumnSelection();
149     SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
150     assertEquals(2, seq.findIndex(seq.getStart()));
151
152     // no hidden columns
153     assertEquals(
154             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
155                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
156                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
157                 seq.findIndex(seq.getEnd()) - 1 }),
158             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
159
160     // hidden column on gap after end of sequence - should not affect bounds
161     cs.hideColumns(13);
162     assertEquals(
163             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
164                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
165                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
166                 seq.findIndex(seq.getEnd()) - 1 }),
167             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
168
169     cs.revealAllHiddenColumns();
170     // hidden column on gap before beginning of sequence - should vis bounds by
171     // one
172     cs.hideColumns(0);
173     assertEquals(
174             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
175                 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
176                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
177                 seq.findIndex(seq.getEnd()) - 1 }),
178             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
179
180     cs.revealAllHiddenColumns();
181     // hide columns around most of sequence - leave one residue remaining
182     cs.hideColumns(1, 3);
183     cs.hideColumns(6, 11);
184     assertEquals("-D",
185             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
186     assertEquals(
187             Arrays.toString(new int[] { 1, 1, 3, 3,
188                 seq.findIndex(seq.getStart()) - 1,
189                 seq.findIndex(seq.getEnd()) - 1 }),
190             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
191     cs.revealAllHiddenColumns();
192
193     // hide whole sequence - should just get location of hidden region
194     // containing sequence
195     cs.hideColumns(1, 11);
196     assertEquals(
197             Arrays.toString(new int[] { 0, 1, 0, 0,
198                 seq.findIndex(seq.getStart()) - 1,
199                 seq.findIndex(seq.getEnd()) - 1 }),
200             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
201
202   }
203
204   @Test(groups = { "Functional" })
205   public void testLocateVisibleBoundsPathologicals()
206   {
207     // test some pathological cases we missed
208     AlignmentI al = new Alignment(new SequenceI[] { new Sequence(
209             "refseqGaptest", "KTDVTI----------NFI-----G----L") });
210     ColumnSelection cs = new ColumnSelection();
211     cs.hideInsertionsFor(al.getSequenceAt(0));
212     assertEquals(
213             "G",
214             ""
215                     + al.getSequenceAt(0).getCharAt(
216                             cs.adjustForHiddenColumns(9)));
217
218   }
219
220   @Test(groups = { "Functional" })
221   public void testHideColumns()
222   {
223     ColumnSelection cs = new ColumnSelection();
224     cs.hideColumns(5);
225     List<int[]> hidden = cs.getHiddenColumns();
226     assertEquals(1, hidden.size());
227     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
228
229     cs.hideColumns(3);
230     assertEquals(2, hidden.size());
231     // two hidden ranges, in order:
232     assertSame(hidden, cs.getHiddenColumns());
233     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
234     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
235
236     // hiding column 4 expands [3, 3] to [3, 4]
237     // and merges to [5, 5] to make [3, 5]
238     cs.hideColumns(4);
239     hidden = cs.getHiddenColumns();
240     assertEquals(1, hidden.size());
241     assertEquals("[3, 5]", Arrays.toString(hidden.get(0)));
242
243     // clear hidden columns (note they are added to selected)
244     cs.revealAllHiddenColumns();
245     // it is now actually null but getter returns an empty list
246     assertTrue(cs.getHiddenColumns().isEmpty());
247
248     cs.hideColumns(3, 6);
249     hidden = cs.getHiddenColumns();
250     int[] firstHiddenRange = hidden.get(0);
251     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
252
253     // adding a subrange of already hidden should do nothing
254     cs.hideColumns(4, 5);
255     assertEquals(1, hidden.size());
256     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
257     cs.hideColumns(3, 5);
258     assertEquals(1, hidden.size());
259     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
260     cs.hideColumns(4, 6);
261     assertEquals(1, hidden.size());
262     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
263     cs.hideColumns(3, 6);
264     assertEquals(1, hidden.size());
265     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
266
267     cs.revealAllHiddenColumns();
268     cs.hideColumns(2, 4);
269     hidden = cs.getHiddenColumns();
270     assertEquals(1, hidden.size());
271     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
272
273     // extend contiguous with 2 positions overlap
274     cs.hideColumns(3, 5);
275     assertEquals(1, hidden.size());
276     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
277
278     // extend contiguous with 1 position overlap
279     cs.hideColumns(5, 6);
280     assertEquals(1, hidden.size());
281     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
282
283     // extend contiguous with overlap both ends:
284     cs.hideColumns(1, 7);
285     assertEquals(1, hidden.size());
286     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
287   }
288
289   /**
290    * Test the method that hides a specified column including any adjacent
291    * selected columns. This is a convenience method for the case where multiple
292    * column regions are selected and then hidden using menu option View | Hide |
293    * Selected Columns.
294    */
295   @Test(groups = { "Functional" })
296   public void testHideColumns_withSelection()
297   {
298     ColumnSelection cs = new ColumnSelection();
299     // select columns 4-6
300     cs.addElement(4);
301     cs.addElement(5);
302     cs.addElement(6);
303     // hide column 5 (and adjacent):
304     cs.hideColumns(5);
305     // 4,5,6 now hidden:
306     List<int[]> hidden = cs.getHiddenColumns();
307     assertEquals(1, hidden.size());
308     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
309     // none now selected:
310     assertTrue(cs.getSelected().isEmpty());
311
312     // repeat, hiding column 4 (5 and 6)
313     cs = new ColumnSelection();
314     cs.addElement(4);
315     cs.addElement(5);
316     cs.addElement(6);
317     cs.hideColumns(4);
318     hidden = cs.getHiddenColumns();
319     assertEquals(1, hidden.size());
320     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
321     assertTrue(cs.getSelected().isEmpty());
322
323     // repeat, hiding column (4, 5 and) 6
324     cs = new ColumnSelection();
325     cs.addElement(4);
326     cs.addElement(5);
327     cs.addElement(6);
328     cs.hideColumns(6);
329     hidden = cs.getHiddenColumns();
330     assertEquals(1, hidden.size());
331     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
332     assertTrue(cs.getSelected().isEmpty());
333
334     // repeat, with _only_ adjacent columns selected
335     cs = new ColumnSelection();
336     cs.addElement(4);
337     cs.addElement(6);
338     cs.hideColumns(5);
339     hidden = cs.getHiddenColumns();
340     assertEquals(1, hidden.size());
341     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
342     assertTrue(cs.getSelected().isEmpty());
343   }
344
345   /**
346    * Test the method that hides all (possibly disjoint) selected column ranges
347    */
348   @Test(groups = { "Functional" })
349   public void testHideSelectedColumns()
350   {
351     ColumnSelection cs = new ColumnSelection();
352     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
353     for (int col : sel)
354     {
355       cs.addElement(col);
356     }
357     cs.hideColumns(15, 18);
358
359     cs.hideSelectedColumns();
360     assertTrue(cs.getSelected().isEmpty());
361     List<int[]> hidden = cs.getHiddenColumns();
362     assertEquals(4, hidden.size());
363     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
364     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
365     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
366     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
367   }
368
369   /**
370    * Test the method that gets runs of selected columns ordered by column. If
371    * this fails, HideSelectedColumns may also fail
372    */
373   @Test(groups = { "Functional" })
374   public void testGetSelectedRanges()
375   {
376     /*
377      * getSelectedRanges returns ordered columns regardless
378      * of the order in which they are added
379      */
380     ColumnSelection cs = new ColumnSelection();
381     int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
382     for (int col : sel)
383     {
384       cs.addElement(col);
385     }
386     List<int[]> range;
387     range = cs.getSelectedRanges();
388     assertEquals(3, range.size());
389     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
390     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
391     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
392     cs.addElement(0);
393     cs.addElement(1);
394     range = cs.getSelectedRanges();
395     assertEquals(3, range.size());
396     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
397   }
398
399   /**
400    * Test the method that reveals a range of hidden columns given the start
401    * column of the range
402    */
403   @Test(groups = { "Functional" })
404   public void testRevealHiddenColumns()
405   {
406     ColumnSelection cs = new ColumnSelection();
407     cs.hideColumns(5, 8);
408     cs.addElement(10);
409     cs.revealHiddenColumns(5);
410     // hidden columns list now null but getter returns empty list:
411     assertTrue(cs.getHiddenColumns().isEmpty());
412     // revealed columns are marked as selected (added to selection):
413     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
414
415     // calling with a column other than the range start does nothing:
416     cs = new ColumnSelection();
417     cs.hideColumns(5, 8);
418     List<int[]> hidden = cs.getHiddenColumns();
419     cs.revealHiddenColumns(6);
420     assertSame(hidden, cs.getHiddenColumns());
421     assertTrue(cs.getSelected().isEmpty());
422   }
423
424   @Test(groups = { "Functional" })
425   public void testRevealAllHiddenColumns()
426   {
427     ColumnSelection cs = new ColumnSelection();
428     cs.hideColumns(5, 8);
429     cs.hideColumns(2, 3);
430     cs.addElement(11);
431     cs.addElement(1);
432     cs.revealAllHiddenColumns();
433
434     /*
435      * revealing hidden columns adds them (in order) to the (unordered)
436      * selection list
437      */
438     assertTrue(cs.getHiddenColumns().isEmpty());
439     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
440   }
441
442   @Test(groups = { "Functional" })
443   public void testIsVisible()
444   {
445     ColumnSelection cs = new ColumnSelection();
446     cs.hideColumns(2, 4);
447     cs.hideColumns(6, 7);
448     assertTrue(cs.isVisible(0));
449     assertTrue(cs.isVisible(-99));
450     assertTrue(cs.isVisible(1));
451     assertFalse(cs.isVisible(2));
452     assertFalse(cs.isVisible(3));
453     assertFalse(cs.isVisible(4));
454     assertTrue(cs.isVisible(5));
455     assertFalse(cs.isVisible(6));
456     assertFalse(cs.isVisible(7));
457   }
458
459   @Test(groups = { "Functional" })
460   public void testGetVisibleContigs()
461   {
462     ColumnSelection cs = new ColumnSelection();
463     cs.hideColumns(3, 6);
464     cs.hideColumns(8, 9);
465     cs.hideColumns(12, 12);
466
467     // start position is inclusive, end position exclusive:
468     int[] visible = cs.getVisibleContigs(1, 13);
469     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
470
471     visible = cs.getVisibleContigs(4, 14);
472     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
473
474     visible = cs.getVisibleContigs(3, 10);
475     assertEquals("[7, 7]", Arrays.toString(visible));
476
477     visible = cs.getVisibleContigs(4, 6);
478     assertEquals("[]", Arrays.toString(visible));
479   }
480
481   @Test(groups = { "Functional" })
482   public void testInvertColumnSelection()
483   {
484     ColumnSelection cs = new ColumnSelection();
485     cs.addElement(4);
486     cs.addElement(6);
487     cs.addElement(8);
488     cs.hideColumns(3, 3);
489     cs.hideColumns(6, 6);
490
491     // invert selection from start (inclusive) to end (exclusive)
492     // hidden columns are _not_ changed
493     cs.invertColumnSelection(2, 9);
494     assertEquals("[2, 5, 7]", cs.getSelected().toString());
495
496     cs.invertColumnSelection(1, 9);
497     assertEquals("[1, 4, 8]", cs.getSelected().toString());
498   }
499
500   @Test(groups = { "Functional" })
501   public void testMaxColumnSelection()
502   {
503     ColumnSelection cs = new ColumnSelection();
504     cs.addElement(0);
505     cs.addElement(513);
506     cs.addElement(1);
507     assertEquals(513, cs.getMax());
508     cs.removeElement(513);
509     assertEquals(1, cs.getMax());
510     cs.removeElement(1);
511     assertEquals(0, cs.getMax());
512     cs.addElement(512);
513     cs.addElement(513);
514     assertEquals(513, cs.getMax());
515
516   }
517
518   @Test(groups = { "Functional" })
519   public void testMinColumnSelection()
520   {
521     ColumnSelection cs = new ColumnSelection();
522     cs.addElement(0);
523     cs.addElement(513);
524     cs.addElement(1);
525     assertEquals(0, cs.getMin());
526     cs.removeElement(0);
527     assertEquals(1, cs.getMin());
528     cs.addElement(0);
529     assertEquals(0, cs.getMin());
530   }
531
532   @Test(groups = { "Functional" })
533   public void testEquals()
534   {
535     ColumnSelection cs = new ColumnSelection();
536     cs.addElement(0);
537     cs.addElement(513);
538     cs.addElement(1);
539     cs.hideColumns(3);
540     cs.hideColumns(7);
541     cs.hideColumns(5, 9);
542
543     // same selections added in a different order
544     ColumnSelection cs2 = new ColumnSelection();
545     cs2.addElement(1);
546     cs2.addElement(513);
547     cs2.addElement(0);
548
549     // with no hidden columns
550     assertFalse(cs.equals(cs2));
551     assertFalse(cs2.equals(cs));
552
553     // with hidden columns added in a different order
554     cs2.hideColumns(6, 9);
555     cs2.hideColumns(5, 8);
556     cs2.hideColumns(3);
557
558     assertTrue(cs.equals(cs2));
559     assertTrue(cs.equals(cs));
560     assertTrue(cs2.equals(cs));
561     assertTrue(cs2.equals(cs2));
562
563     cs2.addElement(12);
564     assertFalse(cs.equals(cs2));
565     assertFalse(cs2.equals(cs));
566
567     cs2.removeElement(12);
568     assertTrue(cs.equals(cs2));
569
570     cs2.hideColumns(88);
571     assertFalse(cs.equals(cs2));
572     /*
573      * unhiding a column adds it to selection!
574      */
575     cs2.revealHiddenColumns(88);
576     assertFalse(cs.equals(cs2));
577     cs.addElement(88);
578     assertTrue(cs.equals(cs2));
579   }
580
581   /**
582    * Test the method that returns selected columns, in the order in which they
583    * were added
584    */
585   @Test(groups = { "Functional" })
586   public void testGetSelected()
587   {
588     ColumnSelection cs = new ColumnSelection();
589     int[] sel = { 4, 3, 7, 21 };
590     for (int col : sel)
591     {
592       cs.addElement(col);
593     }
594
595     List<Integer> selected = cs.getSelected();
596     assertEquals(4, selected.size());
597     assertEquals("[4, 3, 7, 21]", selected.toString());
598
599     /*
600      * getSelected returns a read-only view of the list
601      * verify the view follows any changes in it
602      */
603     cs.removeElement(7);
604     cs.addElement(1);
605     cs.removeElement(4);
606     assertEquals("[3, 21, 1]", selected.toString());
607   }
608
609   /**
610    * Test to verify that the list returned by getSelection cannot be modified
611    */
612   @Test(groups = { "Functional" })
613   public void testGetSelected_isReadOnly()
614   {
615     ColumnSelection cs = new ColumnSelection();
616     cs.addElement(3);
617
618     List<Integer> selected = cs.getSelected();
619     try
620     {
621       selected.clear();
622       fail("expected exception");
623     } catch (UnsupportedOperationException e)
624     {
625       // expected
626     }
627     try
628     {
629       selected.add(1);
630       fail("expected exception");
631     } catch (UnsupportedOperationException e)
632     {
633       // expected
634     }
635     try
636     {
637       selected.remove(3);
638       fail("expected exception");
639     } catch (UnsupportedOperationException e)
640     {
641       // expected
642     }
643     try
644     {
645       Collections.sort(selected);
646       fail("expected exception");
647     } catch (UnsupportedOperationException e)
648     {
649       // expected
650     }
651   }
652
653   /**
654    * Test that demonstrates a ConcurrentModificationException is thrown if you
655    * change the selection while iterating over it
656    */
657   @Test(
658     groups = "Functional",
659     expectedExceptions = { ConcurrentModificationException.class })
660   public void testGetSelected_concurrentModification()
661   {
662     ColumnSelection cs = new ColumnSelection();
663     cs.addElement(0);
664     cs.addElement(1);
665     cs.addElement(2);
666
667     /*
668      * simulate changing the list under us (e.g. in a separate
669      * thread) while iterating over it -> ConcurrentModificationException
670      */
671     List<Integer> selected = cs.getSelected();
672     for (Integer col : selected)
673     {
674       if (col.intValue() == 0)
675       {
676         cs.removeElement(1);
677       }
678     }
679   }
680
681   @Test(groups = "Functional")
682   public void testMarkColumns()
683   {
684     ColumnSelection cs = new ColumnSelection();
685     cs.addElement(5); // this will be cleared
686     BitSet toMark = new BitSet();
687     toMark.set(1);
688     toMark.set(3);
689     toMark.set(6);
690     toMark.set(9);
691
692     assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
693     List<Integer> selected = cs.getSelected();
694     assertEquals(2, selected.size());
695     assertTrue(selected.contains(3));
696     assertTrue(selected.contains(6));
697   }
698
699   @Test(groups = "Functional")
700   public void testMarkColumns_extend()
701   {
702     ColumnSelection cs = new ColumnSelection();
703     cs.addElement(1);
704     cs.addElement(5);
705     BitSet toMark = new BitSet();
706     toMark.set(1);
707     toMark.set(3);
708     toMark.set(6);
709     toMark.set(9);
710
711     /*
712      * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
713      */
714     assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
715     List<Integer> selected = cs.getSelected();
716     assertEquals(4, selected.size());
717     assertTrue(selected.contains(1));
718     assertTrue(selected.contains(3));
719     assertTrue(selected.contains(5));
720     assertTrue(selected.contains(6));
721   }
722
723   @Test(groups = "Functional")
724   public void testMarkColumns_invert()
725   {
726     ColumnSelection cs = new ColumnSelection();
727     cs.addElement(5); // this will be cleared
728     BitSet toMark = new BitSet();
729     toMark.set(1);
730     toMark.set(3);
731     toMark.set(6);
732     toMark.set(9);
733
734     /*
735      * inverted selection of {3, 6} should select {4, 5, 7, 8}
736      */
737     assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
738     List<Integer> selected = cs.getSelected();
739     assertEquals(4, selected.size());
740     assertTrue(selected.contains(4));
741     assertTrue(selected.contains(5));
742     assertTrue(selected.contains(7));
743     assertTrue(selected.contains(8));
744   }
745
746   @Test(groups = "Functional")
747   public void testMarkColumns_toggle()
748   {
749     ColumnSelection cs = new ColumnSelection();
750     cs.addElement(1); // outside change range
751     cs.addElement(3);
752     cs.addElement(4);
753     cs.addElement(10); // outside change range
754     BitSet toMark = new BitSet();
755     toMark.set(1);
756     toMark.set(3);
757     toMark.set(6);
758     toMark.set(9);
759
760     /*
761      * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
762      */
763     assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
764     List<Integer> selected = cs.getSelected();
765     assertEquals(4, selected.size());
766     assertTrue(selected.contains(1));
767     assertTrue(selected.contains(4));
768     assertTrue(selected.contains(6));
769     assertTrue(selected.contains(10));
770   }
771
772   @Test(groups = "Functional")
773   public void testCopyConstructor()
774   {
775     ColumnSelection cs = new ColumnSelection();
776     cs.addElement(3);
777     cs.addElement(1);
778     cs.hideColumns(10, 11);
779     cs.hideColumns(5, 7);
780     assertEquals("[5, 7]", Arrays.toString(cs.getHiddenColumns().get(0)));
781
782     ColumnSelection cs2 = new ColumnSelection(cs);
783     assertTrue(cs2.hasSelectedColumns());
784     assertTrue(cs2.hasHiddenColumns());
785     // order of column selection is preserved
786     assertEquals("[3, 1]", cs2.getSelected().toString());
787     assertEquals(2, cs2.getHiddenColumns().size());
788     // hidden columns are held in column order
789     assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenColumns().get(0)));
790     assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenColumns().get(1)));
791   }
792
793   /**
794    * Test for the case when a hidden range encloses more one already hidden
795    * range
796    */
797   @Test(groups = { "Functional" })
798   public void testHideColumns_subsumingHidden()
799   {
800     /*
801      * JAL-2370 bug scenario:
802      * two hidden ranges subsumed by a third
803      */
804     ColumnSelection cs = new ColumnSelection();
805     cs.hideColumns(49, 59);
806     cs.hideColumns(69, 79);
807     List<int[]> hidden = cs.getHiddenColumns();
808     assertEquals(2, hidden.size());
809     assertEquals("[49, 59]", Arrays.toString(hidden.get(0)));
810     assertEquals("[69, 79]", Arrays.toString(hidden.get(1)));
811   
812     cs.hideColumns(48, 80);
813     hidden = cs.getHiddenColumns();
814     assertEquals(1, hidden.size());
815     assertEquals("[48, 80]", Arrays.toString(hidden.get(0)));
816
817     /*
818      * another...joining hidden ranges
819      */
820     cs = new ColumnSelection();
821     cs.hideColumns(10, 20);
822     cs.hideColumns(30, 40);
823     cs.hideColumns(50, 60);
824     // hiding 21-49 should merge to one range
825     cs.hideColumns(21, 49);
826     hidden = cs.getHiddenColumns();
827     assertEquals(1, hidden.size());
828     assertEquals("[10, 60]", Arrays.toString(hidden.get(0)));
829
830     /*
831      * another...lef overlap, subsumption, right overlap,
832      * no overlap of existing hidden ranges
833      */
834     cs = new ColumnSelection();
835     cs.hideColumns(10, 20);
836     cs.hideColumns(10, 20);
837     cs.hideColumns(30, 35);
838     cs.hideColumns(40, 50);
839     cs.hideColumns(60, 70);
840
841     cs.hideColumns(15, 45);
842     hidden = cs.getHiddenColumns();
843     assertEquals(2, hidden.size());
844     assertEquals("[10, 50]", Arrays.toString(hidden.get(0)));
845     assertEquals("[60, 70]", Arrays.toString(hidden.get(1)));
846   }
847
848   @Test(groups = { "Functional" })
849   public void testStretchGroup_expand()
850   {
851     /*
852      * test that emulates clicking column 4 (selected)
853      * and dragging right to column 5 (all base 0)
854      */
855     ColumnSelection cs = new ColumnSelection();
856     cs.addElement(4);
857     SequenceGroup sg = new SequenceGroup();
858     sg.setStartRes(4);
859     sg.setEndRes(4);
860     cs.stretchGroup(5, sg, 4, 4);
861     assertEquals(cs.getSelected().size(), 2);
862     assertTrue(cs.contains(4));
863     assertTrue(cs.contains(5));
864     assertEquals(sg.getStartRes(), 4);
865     assertEquals(sg.getEndRes(), 5);
866
867     /*
868      * emulate drag right with columns 10-20 already selected
869      */
870     cs.clear();
871     for (int i = 10; i <= 20; i++)
872     {
873       cs.addElement(i);
874     }
875     assertEquals(cs.getSelected().size(), 11);
876     sg = new SequenceGroup();
877     sg.setStartRes(10);
878     sg.setEndRes(20);
879     cs.stretchGroup(21, sg, 10, 20);
880     assertEquals(cs.getSelected().size(), 12);
881     assertTrue(cs.contains(10));
882     assertTrue(cs.contains(21));
883     assertEquals(sg.getStartRes(), 10);
884     assertEquals(sg.getEndRes(), 21);
885   }
886
887   @Test(groups = { "Functional" })
888   public void testStretchGroup_shrink()
889   {
890     /*
891      * emulate drag left to 19 with columns 10-20 already selected
892      */
893     ColumnSelection cs = new ColumnSelection();
894     for (int i = 10; i <= 20; i++)
895     {
896       cs.addElement(i);
897     }
898     assertEquals(cs.getSelected().size(), 11);
899     SequenceGroup sg = new SequenceGroup();
900     sg.setStartRes(10);
901     sg.setEndRes(20);
902     cs.stretchGroup(19, sg, 10, 20);
903     assertEquals(cs.getSelected().size(), 10);
904     assertTrue(cs.contains(10));
905     assertTrue(cs.contains(19));
906     assertFalse(cs.contains(20));
907     assertEquals(sg.getStartRes(), 10);
908     assertEquals(sg.getEndRes(), 19);
909   }
910 }