Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.datamodel

File HiddenColumns.java

 

Coverage histogram

../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

102
289
33
1
1,128
652
98
0.34
8.76
33
2.97

Classes

Class Line # Actions
HiddenColumns 65 289 98 1
0.997641599.8%
 

Contributing tests

This file is covered by 324 tests. .

Source view

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.ArrayList;
24    import java.util.Arrays;
25    import java.util.BitSet;
26    import java.util.Iterator;
27    import java.util.List;
28    import java.util.concurrent.locks.ReentrantReadWriteLock;
29   
30    /**
31    * This class manages the collection of hidden columns associated with an
32    * alignment. To iterate over the collection, or over visible columns/regions,
33    * use an iterator obtained from one of:
34    *
35    * - getBoundedIterator: iterates over the hidden regions, within some bounds,
36    * returning *absolute* positions
37    *
38    * - getBoundedStartIterator: iterates over the start positions of hidden
39    * regions, within some bounds, returning *visible* positions
40    *
41    * - getVisContigsIterator: iterates over visible regions in a range, returning
42    * *absolute* positions
43    *
44    * - getVisibleColsIterator: iterates over the visible *columns*
45    *
46    * For performance reasons, provide bounds where possible. Note that column
47    * numbering begins at 0 throughout this class.
48    *
49    * @author kmourao
50    */
51   
52    /* Implementation notes:
53    *
54    * Methods which change the hiddenColumns collection should use a writeLock to
55    * prevent other threads accessing the hiddenColumns collection while changes
56    * are being made. They should also reset the hidden columns cursor, and either
57    * update the hidden columns count, or set it to 0 (so that it will later be
58    * updated when needed).
59    *
60    *
61    * Methods which only need read access to the hidden columns collection should
62    * use a readLock to prevent other threads changing the hidden columns
63    * collection while it is in use.
64    */
 
65    public class HiddenColumns
66    {
67    private static final int HASH_MULTIPLIER = 31;
68   
69    private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
70   
71    /*
72    * Cursor which tracks the last used hidden columns region, and the number
73    * of hidden columns up to (but not including) that region.
74    */
75    private HiddenColumnsCursor cursor = new HiddenColumnsCursor();
76   
77    /*
78    * cache of the number of hidden columns: must be kept up to date by methods
79    * which add or remove hidden columns
80    */
81    private int numColumns = 0;
82   
83    /*
84    * list of hidden column [start, end] ranges; the list is maintained in
85    * ascending start column order
86    */
87    private List<int[]> hiddenColumns = new ArrayList<>();
88   
89    /**
90    * Constructor
91    */
 
92  1365 toggle public HiddenColumns()
93    {
94    }
95   
96    /**
97    * Copy constructor
98    *
99    * @param copy
100    * the HiddenColumns object to copy from
101    */
 
102  18 toggle public HiddenColumns(HiddenColumns copy)
103    {
104  18 this(copy, Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
105    }
106   
107    /**
108    * Copy constructor within bounds and with offset. Copies hidden column
109    * regions fully contained between start and end, and offsets positions by
110    * subtracting offset.
111    *
112    * @param copy
113    * HiddenColumns instance to copy from
114    * @param start
115    * lower bound to copy from
116    * @param end
117    * upper bound to copy to
118    * @param offset
119    * offset to subtract from each region boundary position
120    *
121    */
 
122  21 toggle public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
123    {
124  21 try
125    {
126  21 LOCK.writeLock().lock();
127  21 if (copy != null)
128    {
129  21 numColumns = 0;
130  21 Iterator<int[]> it = copy.getBoundedIterator(start, end);
131  36 while (it.hasNext())
132    {
133  15 int[] region = it.next();
134    // still need to check boundaries because iterator returns
135    // all overlapping regions and we need contained regions
136  15 if (region[0] >= start && region[1] <= end)
137    {
138  13 hiddenColumns.add(
139    new int[]
140    { region[0] - offset, region[1] - offset });
141  13 numColumns += region[1] - region[0] + 1;
142    }
143    }
144  21 cursor = new HiddenColumnsCursor(hiddenColumns);
145    }
146    } finally
147    {
148  21 LOCK.writeLock().unlock();
149    }
150    }
151   
152    /**
153    * Adds the specified column range to the hidden columns collection
154    *
155    * @param start
156    * start of range to add (absolute position in alignment)
157    * @param end
158    * end of range to add (absolute position in alignment)
159    */
 
160  372 toggle public void hideColumns(int start, int end)
161    {
162  372 try
163    {
164  372 LOCK.writeLock().lock();
165   
166  372 int previndex = 0;
167  372 int prevHiddenCount = 0;
168  372 int regionindex = 0;
169  372 if (!hiddenColumns.isEmpty())
170    {
171    // set up cursor reset values
172  170 HiddenCursorPosition cursorPos = cursor.findRegionForColumn(start, false);
173  170 regionindex = cursorPos.getRegionIndex();
174   
175  170 if (regionindex > 0)
176    {
177    // get previous index and hidden count for updating the cursor later
178  143 previndex = regionindex - 1;
179  143 int[] prevRegion = hiddenColumns.get(previndex);
180  143 prevHiddenCount = cursorPos.getHiddenSoFar()
181    - (prevRegion[1] - prevRegion[0] + 1);
182    }
183    }
184   
185    // new range follows everything else; check first to avoid looping over
186    // whole hiddenColumns collection
187  372 if (hiddenColumns.isEmpty()
188    || start > hiddenColumns.get(hiddenColumns.size() - 1)[1])
189    {
190  337 hiddenColumns.add(new int[] { start, end });
191  337 numColumns += end - start + 1;
192    }
193    else
194    {
195    /*
196    * traverse existing hidden ranges and insert / amend / append as
197    * appropriate
198    */
199  35 boolean added = false;
200  35 if (regionindex > 0)
201    {
202  8 added = insertRangeAtRegion(regionindex - 1, start, end);
203    }
204  35 if (!added && regionindex < hiddenColumns.size())
205    {
206  31 insertRangeAtRegion(regionindex, start, end);
207    }
208    }
209   
210    // reset the cursor to just before our insertion point: this saves
211    // a lot of reprocessing in large alignments
212  372 cursor = new HiddenColumnsCursor(hiddenColumns, previndex,
213    prevHiddenCount);
214    } finally
215    {
216  372 LOCK.writeLock().unlock();
217    }
218    }
219   
220    /**
221    * Insert [start, range] at the region at index i in hiddenColumns, if
222    * feasible
223    *
224    * @param i
225    * index to insert at
226    * @param start
227    * start of range to insert
228    * @param end
229    * end of range to insert
230    * @return true if range was successfully inserted
231    */
 
232  39 toggle private boolean insertRangeAtRegion(int i, int start, int end)
233    {
234  39 boolean added = false;
235   
236  39 int[] region = hiddenColumns.get(i);
237  39 if (end < region[0] - 1)
238    {
239    /*
240    * insert discontiguous preceding range
241    */
242  17 hiddenColumns.add(i, new int[] { start, end });
243  17 numColumns += end - start + 1;
244  17 added = true;
245    }
246  22 else if (end <= region[1])
247    {
248    /*
249    * new range overlaps existing, or is contiguous preceding it - adjust
250    * start column
251    */
252  8 int oldstart = region[0];
253  8 region[0] = Math.min(region[0], start);
254  8 numColumns += oldstart - region[0]; // new columns are between old and
255    // adjusted starts
256  8 added = true;
257    }
258  14 else if (start <= region[1] + 1)
259    {
260    /*
261    * new range overlaps existing, or is contiguous following it - adjust
262    * start and end columns
263    */
264  10 insertRangeAtOverlap(i, start, end, region);
265  10 added = true;
266    }
267  39 return added;
268    }
269   
270    /**
271    * Insert a range whose start position overlaps an existing region and/or is
272    * contiguous to the right of the region
273    *
274    * @param i
275    * index to insert at
276    * @param start
277    * start of range to insert
278    * @param end
279    * end of range to insert
280    * @param region
281    * the overlapped/continued region
282    */
 
283  10 toggle private void insertRangeAtOverlap(int i, int start, int end, int[] region)
284    {
285  10 int oldstart = region[0];
286  10 int oldend = region[1];
287  10 region[0] = Math.min(region[0], start);
288  10 region[1] = Math.max(region[1], end);
289   
290  10 numColumns += oldstart - region[0];
291   
292    /*
293    * also update or remove any subsequent ranges
294    * that are overlapped
295    */
296  10 int endi = i;
297  18 while (endi < hiddenColumns.size() - 1)
298    {
299  10 int[] nextRegion = hiddenColumns.get(endi + 1);
300  10 if (nextRegion[0] > end + 1)
301    {
302    /*
303    * gap to next hidden range - no more to update
304    */
305  2 break;
306    }
307  8 numColumns -= nextRegion[1] - nextRegion[0] + 1;
308  8 region[1] = Math.max(nextRegion[1], end);
309  8 endi++;
310    }
311  10 numColumns += region[1] - oldend;
312  10 hiddenColumns.subList(i + 1, endi + 1).clear();
313    }
314   
315    /**
316    * hide a list of ranges
317    *
318    * @param ranges
319    */
 
320  3 toggle public void hideList(List<int[]> ranges)
321    {
322  3 try
323    {
324  3 LOCK.writeLock().lock();
325  3 for (int[] r : ranges)
326    {
327  9 hideColumns(r[0], r[1]);
328    }
329  3 cursor = new HiddenColumnsCursor(hiddenColumns);
330   
331    } finally
332    {
333  3 LOCK.writeLock().unlock();
334    }
335    }
336   
337    /**
338    * Unhides, and adds to the selection list, all hidden columns
339    */
 
340  140 toggle public void revealAllHiddenColumns(ColumnSelection sel)
341    {
342  140 try
343    {
344  140 LOCK.writeLock().lock();
345   
346  140 for (int[] region : hiddenColumns)
347    {
348  720 for (int j = region[0]; j < region[1] + 1; j++)
349    {
350  629 sel.addElement(j);
351    }
352    }
353  140 hiddenColumns.clear();
354  140 cursor = new HiddenColumnsCursor(hiddenColumns);
355  140 numColumns = 0;
356   
357    } finally
358    {
359  140 LOCK.writeLock().unlock();
360    }
361    }
362   
363    /**
364    * Reveals, and marks as selected, the hidden column range with the given
365    * start column
366    *
367    * @param start
368    * the start column to look for
369    * @param sel
370    * the column selection to add the hidden column range to
371    */
 
372  6 toggle public void revealHiddenColumns(int start, ColumnSelection sel)
373    {
374  6 try
375    {
376  6 LOCK.writeLock().lock();
377   
378  6 if (!hiddenColumns.isEmpty())
379    {
380  5 int regionIndex = cursor.findRegionForColumn(start, false)
381    .getRegionIndex();
382   
383  5 if (regionIndex != -1 && regionIndex != hiddenColumns.size())
384    {
385    // regionIndex is the region which either contains start
386    // or lies to the right of start
387  4 int[] region = hiddenColumns.get(regionIndex);
388  4 if (start == region[0])
389    {
390  14 for (int j = region[0]; j < region[1] + 1; j++)
391    {
392  11 sel.addElement(j);
393    }
394  3 int colsToRemove = region[1] - region[0] + 1;
395  3 hiddenColumns.remove(regionIndex);
396  3 numColumns -= colsToRemove;
397    }
398    }
399    }
400    } finally
401    {
402  6 LOCK.writeLock().unlock();
403    }
404    }
405   
406    /**
407    * Output regions data as a string. String is in the format:
408    * reg0[0]<between>reg0[1]<delimiter>reg1[0]<between>reg1[1] ... regn[1]
409    *
410    * @param delimiter
411    * string to delimit regions
412    * @param betweenstring
413    * to put between start and end region values
414    * @return regions formatted according to delimiter and between strings
415    */
 
416  5 toggle public String regionsToString(String delimiter, String between)
417    {
418  5 try
419    {
420  5 LOCK.readLock().lock();
421  5 StringBuilder regionBuilder = new StringBuilder();
422   
423  5 boolean first = true;
424  5 for (int[] range : hiddenColumns)
425    {
426  13 if (!first)
427    {
428  9 regionBuilder.append(delimiter);
429    }
430    else
431    {
432  4 first = false;
433    }
434  13 regionBuilder.append(range[0]).append(between).append(range[1]);
435   
436    }
437   
438  5 return regionBuilder.toString();
439    } finally
440    {
441  5 LOCK.readLock().unlock();
442    }
443    }
444   
445    /**
446    * Find the number of hidden columns
447    *
448    * @return number of hidden columns
449    */
 
450  2511 toggle public int getSize()
451    {
452  2511 return numColumns;
453    }
454   
455    /**
456    * Get the number of distinct hidden regions
457    *
458    * @return number of regions
459    */
 
460  43 toggle public int getNumberOfRegions()
461    {
462  43 try
463    {
464  43 LOCK.readLock().lock();
465  43 return hiddenColumns.size();
466    } finally
467    {
468  43 LOCK.readLock().unlock();
469    }
470    }
471   
 
472  18 toggle @Override
473    public boolean equals(Object obj)
474    {
475  18 try
476    {
477  18 LOCK.readLock().lock();
478   
479  18 if (!(obj instanceof HiddenColumns))
480    {
481  1 return false;
482    }
483  17 HiddenColumns that = (HiddenColumns) obj;
484   
485    /*
486    * check hidden columns are either both null, or match
487    */
488   
489  17 if (that.hiddenColumns.size() != this.hiddenColumns.size())
490    {
491  2 return false;
492    }
493   
494  15 Iterator<int[]> it = this.iterator();
495  15 Iterator<int[]> thatit = that.iterator();
496  37 while (it.hasNext())
497    {
498  26 if (!(Arrays.equals(it.next(), thatit.next())))
499    {
500  4 return false;
501    }
502    }
503  11 return true;
504   
505    } finally
506    {
507  18 LOCK.readLock().unlock();
508    }
509    }
510   
511    /**
512    * Return absolute column index for a visible column index
513    *
514    * @param column
515    * int column index in alignment view (count from zero)
516    * @return alignment column index for column
517    */
 
518  212380 toggle public int visibleToAbsoluteColumn(int column)
519    {
520  212380 try
521    {
522  212380 LOCK.readLock().lock();
523  212380 int result = column;
524   
525  212380 if (!hiddenColumns.isEmpty())
526    {
527  208138 result += cursor.findRegionForColumn(column, true)
528    .getHiddenSoFar();
529    }
530   
531  212380 return result;
532    } finally
533    {
534  212380 LOCK.readLock().unlock();
535    }
536    }
537   
538    /**
539    * Use this method to find out where a column will appear in the visible
540    * alignment when hidden columns exist. If the column is not visible, then the
541    * index of the next visible column on the left will be returned (or 0 if
542    * there is no visible column on the left)
543    *
544    * @param hiddenColumn
545    * the column index in the full alignment including hidden columns
546    * @return the position of the column in the visible alignment
547    */
 
548  650 toggle public int absoluteToVisibleColumn(int hiddenColumn)
549    {
550  650 try
551    {
552  650 LOCK.readLock().lock();
553  650 int result = hiddenColumn;
554   
555  650 if (!hiddenColumns.isEmpty())
556    {
557  447 HiddenCursorPosition cursorPos = cursor
558    .findRegionForColumn(hiddenColumn, false);
559  447 int index = cursorPos.getRegionIndex();
560  447 int hiddenBeforeCol = cursorPos.getHiddenSoFar();
561   
562    // just subtract hidden cols count - this works fine if column is
563    // visible
564  447 result = hiddenColumn - hiddenBeforeCol;
565   
566    // now check in case column is hidden - it will be in the returned
567    // hidden region
568  447 if (index < hiddenColumns.size())
569    {
570  44 int[] region = hiddenColumns.get(index);
571  44 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
572    {
573    // actually col is hidden, return region[0]-1
574    // unless region[0]==0 in which case return 0
575  14 if (region[0] == 0)
576    {
577  3 result = 0;
578    }
579    else
580    {
581  11 result = region[0] - 1 - hiddenBeforeCol;
582    }
583    }
584    }
585    }
586   
587  650 return result; // return the shifted position after removing hidden
588    // columns.
589    } finally
590    {
591  650 LOCK.readLock().unlock();
592    }
593    }
594   
595    /**
596    * Find the visible column which is a given visible number of columns to the
597    * left (negative visibleDistance) or right (positive visibleDistance) of
598    * startColumn. If startColumn is not visible, we use the visible column at
599    * the left boundary of the hidden region containing startColumn.
600    *
601    * @param visibleDistance
602    * the number of visible columns to offset by (left offset = negative
603    * value; right offset = positive value)
604    * @param startColumn
605    * the position of the column to start from (absolute position)
606    * @return the position of the column which is <visibleDistance> away
607    * (absolute position)
608    */
 
609  81 toggle public int offsetByVisibleColumns(int visibleDistance, int startColumn)
610    {
611  81 try
612    {
613  81 LOCK.readLock().lock();
614  81 int start = absoluteToVisibleColumn(startColumn);
615  81 return visibleToAbsoluteColumn(start + visibleDistance);
616   
617    } finally
618    {
619  81 LOCK.readLock().unlock();
620    }
621    }
622   
623    /**
624    * This method returns the rightmost limit of a region of an alignment with
625    * hidden columns. In otherwords, the next hidden column.
626    *
627    * @param alPos
628    * the absolute (visible) alignmentPosition to find the next hidden
629    * column for
630    * @return the index of the next hidden column, or alPos if there is no next
631    * hidden column
632    */
 
633  13 toggle public int getNextHiddenBoundary(boolean left, int alPos)
634    {
635  13 try
636    {
637  13 LOCK.readLock().lock();
638  13 if (!hiddenColumns.isEmpty())
639    {
640  11 int index = cursor.findRegionForColumn(alPos, false)
641    .getRegionIndex();
642   
643  11 if (left && index > 0)
644    {
645  3 int[] region = hiddenColumns.get(index - 1);
646  3 return region[1];
647    }
648  8 else if (!left && index < hiddenColumns.size())
649    {
650  4 int[] region = hiddenColumns.get(index);
651  4 if (alPos < region[0])
652    {
653  2 return region[0];
654    }
655  2 else if ((alPos <= region[1])
656    && (index + 1 < hiddenColumns.size()))
657    {
658    // alPos is within a hidden region, return the next one
659    // if there is one
660  1 region = hiddenColumns.get(index + 1);
661  1 return region[0];
662    }
663    }
664    }
665  7 return alPos;
666    } finally
667    {
668  13 LOCK.readLock().unlock();
669    }
670    }
671   
672    /**
673    * Answers if a column in the alignment is visible
674    *
675    * @param column
676    * absolute position of column in the alignment
677    * @return true if column is visible
678    */
 
679  3618 toggle public boolean isVisible(int column)
680    {
681  3618 try
682    {
683  3618 LOCK.readLock().lock();
684   
685  3618 if (!hiddenColumns.isEmpty())
686    {
687  62 int regionindex = cursor.findRegionForColumn(column, false)
688    .getRegionIndex();
689  62 if (regionindex > -1 && regionindex < hiddenColumns.size())
690    {
691  39 int[] region = hiddenColumns.get(regionindex);
692    // already know that column <= region[1] as cursor returns containing
693    // region or region to right
694  39 if (column >= region[0])
695    {
696  21 return false;
697    }
698    }
699    }
700  3597 return true;
701   
702    } finally
703    {
704  3618 LOCK.readLock().unlock();
705    }
706    }
707   
708    /**
709    *
710    * @return true if there are columns hidden
711    */
 
712  5285 toggle public boolean hasHiddenColumns()
713    {
714  5285 try
715    {
716  5285 LOCK.readLock().lock();
717   
718    // we don't use getSize()>0 here because it has to iterate over
719    // the full hiddenColumns collection and so will be much slower
720  5285 return (!hiddenColumns.isEmpty());
721    } finally
722    {
723  5285 LOCK.readLock().unlock();
724    }
725    }
726   
727    /**
728    *
729    * @return true if there is more than one hidden column region
730    */
 
731  4 toggle public boolean hasMultiHiddenColumnRegions()
732    {
733  4 try
734    {
735  4 LOCK.readLock().lock();
736  4 return !hiddenColumns.isEmpty() && hiddenColumns.size() > 1;
737    } finally
738    {
739  4 LOCK.readLock().unlock();
740    }
741    }
742   
743   
744    /**
745    * Returns a hashCode built from hidden column ranges
746    */
 
747  3 toggle @Override
748    public int hashCode()
749    {
750  3 try
751    {
752  3 LOCK.readLock().lock();
753  3 int hashCode = 1;
754   
755  3 for (int[] hidden : hiddenColumns)
756    {
757  5 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
758  5 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
759    }
760  3 return hashCode;
761    } finally
762    {
763  3 LOCK.readLock().unlock();
764    }
765    }
766   
767    /**
768    * Hide columns corresponding to the marked bits
769    *
770    * @param inserts
771    * - columns mapped to bits starting from zero
772    */
 
773  10 toggle public void hideColumns(BitSet inserts)
774    {
775  10 hideColumns(inserts, 0, inserts.length() - 1);
776    }
777   
778    /**
779    * Hide columns corresponding to the marked bits, within the range
780    * [start,end]. Entries in tohide which are outside [start,end] are ignored.
781    *
782    * @param tohide
783    * columns mapped to bits starting from zero
784    * @param start
785    * start of range to hide columns within
786    * @param end
787    * end of range to hide columns within
788    */
 
789  18 toggle private void hideColumns(BitSet tohide, int start, int end)
790    {
791  18 try
792    {
793  18 LOCK.writeLock().lock();
794  18 for (int firstSet = tohide
795  39 .nextSetBit(start), lastSet = start; firstSet >= start
796    && lastSet <= end; firstSet = tohide
797    .nextSetBit(lastSet))
798    {
799  21 lastSet = tohide.nextClearBit(firstSet);
800  21 if (lastSet <= end)
801    {
802  12 hideColumns(firstSet, lastSet - 1);
803    }
804  9 else if (firstSet <= end)
805    {
806  8 hideColumns(firstSet, end);
807    }
808    }
809  18 cursor = new HiddenColumnsCursor(hiddenColumns);
810    } finally
811    {
812  18 LOCK.writeLock().unlock();
813    }
814    }
815   
816    /**
817    * Hide columns corresponding to the marked bits, within the range
818    * [start,end]. Entries in tohide which are outside [start,end] are ignored.
819    * NB Existing entries in [start,end] are cleared.
820    *
821    * @param tohide
822    * columns mapped to bits starting from zero
823    * @param start
824    * start of range to hide columns within
825    * @param end
826    * end of range to hide columns within
827    */
 
828  8 toggle public void clearAndHideColumns(BitSet tohide, int start, int end)
829    {
830  8 clearHiddenColumnsInRange(start, end);
831  8 hideColumns(tohide, start, end);
832    }
833   
834    /**
835    * Make all columns in the range [start,end] visible
836    *
837    * @param start
838    * start of range to show columns
839    * @param end
840    * end of range to show columns
841    */
 
842  8 toggle private void clearHiddenColumnsInRange(int start, int end)
843    {
844  8 try
845    {
846  8 LOCK.writeLock().lock();
847   
848  8 if (!hiddenColumns.isEmpty())
849    {
850  7 HiddenCursorPosition pos = cursor.findRegionForColumn(start, false);
851  7 int index = pos.getRegionIndex();
852   
853  7 if (index != -1 && index != hiddenColumns.size())
854    {
855    // regionIndex is the region which either contains start
856    // or lies to the right of start
857  5 int[] region = hiddenColumns.get(index);
858  5 if (region[0] < start && region[1] >= start)
859    {
860    // region contains start, truncate so that it ends just before start
861  2 numColumns -= region[1] - start + 1;
862  2 region[1] = start - 1;
863  2 index++;
864    }
865   
866  5 int endi = index;
867  11 while (endi < hiddenColumns.size())
868    {
869  8 region = hiddenColumns.get(endi);
870   
871  8 if (region[1] > end)
872    {
873  2 if (region[0] <= end)
874    {
875    // region contains end, truncate so it starts just after end
876  1 numColumns -= end - region[0] + 1;
877  1 region[0] = end + 1;
878    }
879  2 break;
880    }
881   
882  6 numColumns -= region[1] - region[0] + 1;
883  6 endi++;
884    }
885  5 hiddenColumns.subList(index, endi).clear();
886   
887    }
888   
889  7 cursor = new HiddenColumnsCursor(hiddenColumns);
890    }
891    } finally
892    {
893  8 LOCK.writeLock().unlock();
894    }
895    }
896   
897    /**
898    *
899    * @param updates
900    * BitSet where hidden columns will be marked
901    */
 
902  2 toggle protected void andNot(BitSet updates)
903    {
904  2 try
905    {
906  2 LOCK.writeLock().lock();
907   
908  2 BitSet hiddenBitSet = new BitSet();
909  2 for (int[] range : hiddenColumns)
910    {
911  2 hiddenBitSet.set(range[0], range[1] + 1);
912    }
913  2 hiddenBitSet.andNot(updates);
914  2 hiddenColumns.clear();
915  2 hideColumns(hiddenBitSet);
916    } finally
917    {
918  2 LOCK.writeLock().unlock();
919    }
920    }
921   
922    /**
923    * Calculate the visible start and end index of an alignment.
924    *
925    * @param width
926    * full alignment width
927    * @return integer array where: int[0] = startIndex, and int[1] = endIndex
928    */
 
929  5 toggle public int[] getVisibleStartAndEndIndex(int width)
930    {
931  5 try
932    {
933  5 LOCK.readLock().lock();
934   
935  5 int firstVisible = 0;
936  5 int lastVisible = width - 1;
937   
938  5 if (!hiddenColumns.isEmpty())
939    {
940    // first visible col with index 0, convert to absolute index
941  4 firstVisible = visibleToAbsoluteColumn(0);
942   
943    // last visible column is either immediately to left of
944    // last hidden region, or is just the last column in the alignment
945  4 int[] lastregion = hiddenColumns.get(hiddenColumns.size() - 1);
946  4 if (lastregion[1] == width - 1)
947    {
948    // last region is at very end of alignment
949    // last visible column immediately precedes it
950  1 lastVisible = lastregion[0] - 1;
951    }
952    }
953  5 return new int[] { firstVisible, lastVisible };
954   
955    } finally
956    {
957  5 LOCK.readLock().unlock();
958    }
959    }
960   
961    /**
962    * Finds the hidden region (if any) which starts or ends at res
963    *
964    * @param res
965    * visible residue position, unadjusted for hidden columns
966    * @return region as [start,end] or null if no matching region is found. If
967    * res is adjacent to two regions, returns the left region.
968    */
 
969  9 toggle public int[] getRegionWithEdgeAtRes(int res)
970    {
971  9 try
972    {
973  9 LOCK.readLock().lock();
974  9 int adjres = visibleToAbsoluteColumn(res);
975   
976  9 int[] reveal = null;
977   
978  9 if (!hiddenColumns.isEmpty())
979    {
980    // look for a region ending just before adjres
981  8 int regionindex = cursor.findRegionForColumn(adjres - 1, false)
982    .getRegionIndex();
983  8 if (regionindex < hiddenColumns.size()
984    && hiddenColumns.get(regionindex)[1] == adjres - 1)
985    {
986  2 reveal = hiddenColumns.get(regionindex);
987    }
988    // check if the region ends just after adjres
989  6 else if (regionindex < hiddenColumns.size()
990    && hiddenColumns.get(regionindex)[0] == adjres + 1)
991    {
992  3 reveal = hiddenColumns.get(regionindex);
993    }
994    }
995  9 return reveal;
996   
997    } finally
998    {
999  9 LOCK.readLock().unlock();
1000    }
1001    }
1002   
1003    /**
1004    * Return an iterator over the hidden regions
1005    */
 
1006  184 toggle public Iterator<int[]> iterator()
1007    {
1008  184 try
1009    {
1010  184 LOCK.readLock().lock();
1011  184 return new RangeIterator(hiddenColumns);
1012    } finally
1013    {
1014  184 LOCK.readLock().unlock();
1015    }
1016    }
1017   
1018    /**
1019    * Return a bounded iterator over the hidden regions
1020    *
1021    * @param start
1022    * position to start from (inclusive, absolute column position)
1023    * @param end
1024    * position to end at (inclusive, absolute column position)
1025    * @return
1026    */
 
1027  47 toggle public Iterator<int[]> getBoundedIterator(int start, int end)
1028    {
1029  47 try
1030    {
1031  47 LOCK.readLock().lock();
1032  47 return new RangeIterator(start, end, hiddenColumns);
1033    } finally
1034    {
1035  47 LOCK.readLock().unlock();
1036    }
1037    }
1038   
1039    /**
1040    * Return a bounded iterator over the *visible* start positions of hidden
1041    * regions
1042    *
1043    * @param start
1044    * position to start from (inclusive, visible column position)
1045    * @param end
1046    * position to end at (inclusive, visible column position)
1047    */
 
1048  435 toggle public Iterator<Integer> getStartRegionIterator(int start, int end)
1049    {
1050  435 try
1051    {
1052  435 LOCK.readLock().lock();
1053   
1054    // get absolute position of column in alignment
1055  435 int absoluteStart = visibleToAbsoluteColumn(start);
1056   
1057    // Get cursor position and supply it to the iterator:
1058    // Since we want visible region start, we look for a cursor for the
1059    // (absoluteStart-1), then if absoluteStart is the start of a visible
1060    // region we'll get the cursor pointing to the region before, which is
1061    // what we want
1062  435 HiddenCursorPosition pos = cursor
1063    .findRegionForColumn(absoluteStart - 1, false);
1064   
1065  435 return new StartRegionIterator(pos, start, end,
1066    hiddenColumns);
1067    } finally
1068    {
1069  435 LOCK.readLock().unlock();
1070    }
1071    }
1072   
1073    /**
1074    * Return an iterator over visible *columns* (not regions) between the given
1075    * start and end boundaries
1076    *
1077    * @param start
1078    * first column (inclusive)
1079    * @param end
1080    * last column (inclusive)
1081    */
 
1082  10 toggle public Iterator<Integer> getVisibleColsIterator(int start, int end)
1083    {
1084  10 try
1085    {
1086  10 LOCK.readLock().lock();
1087  10 return new RangeElementsIterator(
1088    new VisibleContigsIterator(start, end + 1, hiddenColumns));
1089    } finally
1090    {
1091  10 LOCK.readLock().unlock();
1092    }
1093    }
1094   
1095    /**
1096    * return an iterator over visible segments between the given start and end
1097    * boundaries
1098    *
1099    * @param start
1100    * first column, inclusive from 0
1101    * @param end
1102    * last column - not inclusive
1103    * @param useVisibleCoords
1104    * if true, start and end are visible column positions, not absolute
1105    * positions*
1106    */
 
1107  657 toggle public VisibleContigsIterator getVisContigsIterator(int start,
1108    int end,
1109    boolean useVisibleCoords)
1110    {
1111  657 int adjstart = start;
1112  657 int adjend = end;
1113  657 if (useVisibleCoords)
1114    {
1115  432 adjstart = visibleToAbsoluteColumn(start);
1116  432 adjend = visibleToAbsoluteColumn(end);
1117    }
1118   
1119  657 try
1120    {
1121  657 LOCK.readLock().lock();
1122  657 return new VisibleContigsIterator(adjstart, adjend, hiddenColumns);
1123    } finally
1124    {
1125  657 LOCK.readLock().unlock();
1126    }
1127    }
1128    }