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