JAL-2759 HiddenColumnsCursor tests
[jalview.git] / src / jalview / datamodel / HiddenColumnsCursor.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 java.util.List;
24
25 public class HiddenColumnsCursor
26 {
27   // absolute position of first hidden column
28   private int firstColumn;
29
30   // index of last visited region
31   private int regionIndex;
32
33   // number of hidden columns before last visited region
34   private int hiddenSoFar;
35
36   private List<int[]> hiddenColumns;
37
38   protected HiddenColumnsCursor()
39   {
40
41   }
42
43   /**
44    * Set the cursor to a position
45    * 
46    * @param first
47    *          absolute position of first hidden column
48    * @param last
49    *          absolute position of last hidden column
50    * @param index
51    *          index of last visited region
52    * @param hiddenCount
53    *          number of hidden columns before last visited region
54    */
55   protected void resetCursor(List<int[]> hiddenCols)
56   {
57     synchronized (this)
58     {
59       if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
60       {
61         hiddenColumns = hiddenCols;
62         firstColumn = hiddenColumns.get(0)[0];
63         regionIndex = 0;
64         hiddenSoFar = 0;
65       }
66     }
67   }
68
69   protected void updateCursor(int index, int hiddenCount)
70   {
71     synchronized (this)
72     {
73       regionIndex = index;
74       hiddenSoFar = hiddenCount;
75     }
76   }
77
78   protected synchronized int getIndex()
79   {
80     return regionIndex;
81   }
82
83   protected synchronized int getHiddenSoFar()
84   {
85     return hiddenSoFar;
86   }
87
88
89   /**
90    * Get the index of the region that column is within (if column is hidden) or
91    * which is to the right of column (if column is visible). If no hidden
92    * columns are to the right, will return size of hiddenColumns. If hidden
93    * columns is empty returns -1.
94    * 
95    * @param column
96    *          absolute position of a column in the alignment
97    * @return region index
98    */
99   protected int findRegionForColumn(int column)
100   {
101     if (hiddenColumns == null)
102     {
103       return -1;
104     }
105
106     int index = regionIndex;
107     int hiddenCount = hiddenSoFar;
108
109     if (index == hiddenColumns.size())
110     {
111       // went past the end of hiddenColumns collection last time
112       index--;
113       int[] region = hiddenColumns.get(index);
114       hiddenCount -= region[1] - region[0] + 1;
115     }
116
117     if ((hiddenColumns.get(index)[0] <= column)
118             && hiddenColumns.get(index)[1] >= column)
119     {
120       // column is in the current region
121       // we hit the jackpot
122       // don't need to move index
123     }
124     else if (column < firstColumn)
125     {
126       index = 0;
127       hiddenCount = 0;
128     }
129     // column is after current region
130     else if (column > hiddenColumns.get(index)[1]) // includes if column >
131                                                    // lastColumn
132     {
133       // iterate from where we are now, if we're lucky we'll be close by
134       // (but still better than iterating from 0)
135       // stop when we find the region *before* column
136       // i.e. the next region starts after column or if not, ends after column
137       while ((index < hiddenColumns.size())
138               && (column > hiddenColumns.get(index)[1]))
139       {
140         int[] region = hiddenColumns.get(index);
141         hiddenCount += region[1] - region[0] + 1;
142         index++;
143       }
144     }
145
146     // column is before current region
147     else if (column < hiddenColumns.get(index)[0])
148     {
149       // column is before or in the previous region
150       if ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column))
151       {
152         while ((index > 0) && (hiddenColumns.get(index)[1] > column))
153         {
154           index--;
155           int[] region = hiddenColumns.get(index);
156           hiddenCount -= region[1] - region[0] + 1;
157         }
158       }
159     }
160     updateCursor(index, hiddenCount);
161     return index;
162   }
163
164   /**
165    * Get the number of hidden columns in regions before column i.e. excludes
166    * hidden columns in the region column is in, if any
167    * 
168    * @param column
169    *          index of column in visible alignment
170    * @return
171    */
172   protected int getHiddenOffset(int column)
173   {
174     if (hiddenColumns == null)
175     {
176       return -1;
177     }
178
179     int index = getIndex();
180     int hiddenCount = getHiddenSoFar();
181
182     if (column < firstColumn)
183     {
184       index = 0;
185       hiddenCount = 0;
186     }
187     else if ((index < hiddenColumns.size())
188             && (hiddenColumns.get(index)[0] <= column + hiddenCount))
189     {
190       // iterate from where we are now, if we're lucky we'll be close by
191       // (but still better than iterating from 0)
192       while ((index < hiddenColumns.size())
193               && (hiddenColumns.get(index)[0] <= column + hiddenCount))
194       {
195         int[] region = hiddenColumns.get(index);
196         hiddenCount += region[1] - region[0] + 1;
197         index++;
198       }
199     }
200     else
201     {
202       while ((index > 0)
203               && (hiddenColumns.get(index - 1)[1] >= column + hiddenCount))
204       {
205         index--;
206         int[] region = hiddenColumns.get(index);
207         hiddenCount -= region[1] - region[0] + 1;
208       }
209
210     }
211     updateCursor(index, hiddenCount);
212     return hiddenCount;
213   }
214 }