Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  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,132
652
98
0.34
8.76
33
2.97

Classes

Class Line # Actions
HiddenColumns 65 289 98
0.997641599.8%
 

Contributing tests

This file is covered by 369 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  1391 toggle public HiddenColumns()
93    {
94    }
95   
96    /**
97    * Copy constructor
98    *
99    * @param copy
100    * the HiddenColumns object to copy from
101    */
 
102  26 toggle public HiddenColumns(HiddenColumns copy)
103    {
104  26 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  29 toggle public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
123    {
124  29 try
125    {
126  29 LOCK.writeLock().lock();
127  29 if (copy != null)
128    {
129  29 numColumns = 0;
130  29 Iterator<int[]> it = copy.getBoundedIterator(start, end);
131  50 while (it.hasNext())
132    {
133  21 int[] region = it.next();
134    // still need to check boundaries because iterator returns
135    // all overlapping regions and we need contained regions
136  21 if (region[0] >= start && region[1] <= end)
137    {
138  19 hiddenColumns.add(
139    new int[]
140    { region[0] - offset, region[1] - offset });
141  19 numColumns += region[1] - region[0] + 1;
142    }
143    }
144  29 cursor = new HiddenColumnsCursor(hiddenColumns);
145    }
146    } finally
147    {
148  29 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  409 toggle public void hideColumns(int start, int end)
161    {
162  409 try
163    {
164  409 LOCK.writeLock().lock();
165   
166  409 int previndex = 0;
167  409 int prevHiddenCount = 0;
168  409 int regionindex = 0;
169  409 if (!hiddenColumns.isEmpty())
170    {
171    // set up cursor reset values
172  198 HiddenCursorPosition cursorPos = cursor.findRegionForColumn(start, false);
173  198 regionindex = cursorPos.getRegionIndex();
174   
175  198 if (regionindex > 0)
176    {
177    // get previous index and hidden count for updating the cursor later
178  170 previndex = regionindex - 1;
179  170 int[] prevRegion = hiddenColumns.get(previndex);
180  170 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  409 if (hiddenColumns.isEmpty()
188    || start > hiddenColumns.get(hiddenColumns.size() - 1)[1])
189    {
190  373 hiddenColumns.add(new int[] { start, end });
191  373 numColumns += end - start + 1;
192    }
193    else
194    {
195    /*
196    * traverse existing hidden ranges and insert / amend / append as
197    * appropriate
198    */
199  36 boolean added = false;
200  36 if (regionindex > 0)
201    {
202  8 added = insertRangeAtRegion(regionindex - 1, start, end);
203    }
204  36 if (!added && regionindex < hiddenColumns.size())
205    {
206  32 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  409 cursor = new HiddenColumnsCursor(hiddenColumns, previndex,
213    prevHiddenCount);
214    } finally
215    {
216  409 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  40 toggle private boolean insertRangeAtRegion(int i, int start, int end)
233    {
234  40 boolean added = false;
235   
236  40 int[] region = hiddenColumns.get(i);
237  40 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  23 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  15 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  11 insertRangeAtOverlap(i, start, end, region);
265  11 added = true;
266    }
267  40 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  11 toggle private void insertRangeAtOverlap(int i, int start, int end, int[] region)
284    {
285  11 int oldstart = region[0];
286  11 int oldend = region[1];
287  11 region[0] = Math.min(region[0], start);
288  11 region[1] = Math.max(region[1], end);
289   
290  11 numColumns += oldstart - region[0];
291   
292    /*
293    * also update or remove any subsequent ranges
294    * that are overlapped
295    */
296  11 int endi = i;
297  19 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  11 numColumns += region[1] - oldend;
312  11 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  141 toggle public void revealAllHiddenColumns(ColumnSelection sel)
341    {
342  141 try
343    {
344  141 LOCK.writeLock().lock();
345   
346  141 for (int[] region : hiddenColumns)
347    {
348  725 for (int j = region[0]; j < region[1] + 1; j++)
349    {
350  633 sel.addElement(j);
351    }
352    }
353  141 hiddenColumns.clear();
354  141 cursor = new HiddenColumnsCursor(hiddenColumns);
355  141 numColumns = 0;
356   
357    } finally
358    {
359  141 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  3714 toggle public int getSize()
451    {
452  3714 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    /**
473    * Answers true if obj is an instance of HiddenColumns, and holds the same
474    * array of start-end column ranges as this, else answers false
475    */
 
476  56 toggle @Override
477    public boolean equals(Object obj)
478    {
479  56 try
480    {
481  56 LOCK.readLock().lock();
482   
483  56 if (!(obj instanceof HiddenColumns))
484    {
485  2 return false;
486    }
487  54 HiddenColumns that = (HiddenColumns) obj;
488   
489    /*
490    * check hidden columns are either both null, or match
491    */
492   
493  54 if (that.hiddenColumns.size() != this.hiddenColumns.size())
494    {
495  16 return false;
496    }
497   
498  38 Iterator<int[]> it = this.iterator();
499  38 Iterator<int[]> thatit = that.iterator();
500  63 while (it.hasNext())
501    {
502  44 if (!(Arrays.equals(it.next(), thatit.next())))
503    {
504  19 return false;
505    }
506    }
507  19 return true;
508   
509    } finally
510    {
511  56 LOCK.readLock().unlock();
512    }
513    }
514   
515    /**
516    * Return absolute column index for a visible column index
517    *
518    * @param column
519    * int column index in alignment view (count from zero)
520    * @return alignment column index for column
521    */
 
522  162994 toggle public int visibleToAbsoluteColumn(int column)
523    {
524  162994 try
525    {
526  162994 LOCK.readLock().lock();
527  162994 int result = column;
528   
529  162994 if (!hiddenColumns.isEmpty())
530    {
531  159143 result += cursor.findRegionForColumn(column, true)
532    .getHiddenSoFar();
533    }
534   
535  162994 return result;
536    } finally
537    {
538  162994 LOCK.readLock().unlock();
539    }
540    }
541   
542    /**
543    * Use this method to find out where a column will appear in the visible
544    * alignment when hidden columns exist. If the column is not visible, then the
545    * index of the next visible column on the left will be returned (or 0 if
546    * there is no visible column on the left)
547    *
548    * @param hiddenColumn
549    * the column index in the full alignment including hidden columns
550    * @return the position of the column in the visible alignment
551    */
 
552  339 toggle public int absoluteToVisibleColumn(int hiddenColumn)
553    {
554  339 try
555    {
556  339 LOCK.readLock().lock();
557  339 int result = hiddenColumn;
558   
559  339 if (!hiddenColumns.isEmpty())
560    {
561  93 HiddenCursorPosition cursorPos = cursor
562    .findRegionForColumn(hiddenColumn, false);
563  93 int index = cursorPos.getRegionIndex();
564  93 int hiddenBeforeCol = cursorPos.getHiddenSoFar();
565   
566    // just subtract hidden cols count - this works fine if column is
567    // visible
568  93 result = hiddenColumn - hiddenBeforeCol;
569   
570    // now check in case column is hidden - it will be in the returned
571    // hidden region
572  93 if (index < hiddenColumns.size())
573    {
574  52 int[] region = hiddenColumns.get(index);
575  52 if (hiddenColumn >= region[0] && hiddenColumn <= region[1])
576    {
577    // actually col is hidden, return region[0]-1
578    // unless region[0]==0 in which case return 0
579  14 if (region[0] == 0)
580    {
581  3 result = 0;
582    }
583    else
584    {
585  11 result = region[0] - 1 - hiddenBeforeCol;
586    }
587    }
588    }
589    }
590   
591  339 return result; // return the shifted position after removing hidden
592    // columns.
593    } finally
594    {
595  339 LOCK.readLock().unlock();
596    }
597    }
598   
599    /**
600    * Find the visible column which is a given visible number of columns to the
601    * left (negative visibleDistance) or right (positive visibleDistance) of
602    * startColumn. If startColumn is not visible, we use the visible column at
603    * the left boundary of the hidden region containing startColumn.
604    *
605    * @param visibleDistance
606    * the number of visible columns to offset by (left offset = negative
607    * value; right offset = positive value)
608    * @param startColumn
609    * the position of the column to start from (absolute position)
610    * @return the position of the column which is <visibleDistance> away
611    * (absolute position)
612    */
 
613  81 toggle public int offsetByVisibleColumns(int visibleDistance, int startColumn)
614    {
615  81 try
616    {
617  81 LOCK.readLock().lock();
618  81 int start = absoluteToVisibleColumn(startColumn);
619  81 return visibleToAbsoluteColumn(start + visibleDistance);
620   
621    } finally
622    {
623  81 LOCK.readLock().unlock();
624    }
625    }
626   
627    /**
628    * This method returns the rightmost limit of a region of an alignment with
629    * hidden columns. In otherwords, the next hidden column.
630    *
631    * @param alPos
632    * the absolute (visible) alignmentPosition to find the next hidden
633    * column for
634    * @return the index of the next hidden column, or alPos if there is no next
635    * hidden column
636    */
 
637  13 toggle public int getNextHiddenBoundary(boolean left, int alPos)
638    {
639  13 try
640    {
641  13 LOCK.readLock().lock();
642  13 if (!hiddenColumns.isEmpty())
643    {
644  11 int index = cursor.findRegionForColumn(alPos, false)
645    .getRegionIndex();
646   
647  11 if (left && index > 0)
648    {
649  3 int[] region = hiddenColumns.get(index - 1);
650  3 return region[1];
651    }
652  8 else if (!left && index < hiddenColumns.size())
653    {
654  4 int[] region = hiddenColumns.get(index);
655  4 if (alPos < region[0])
656    {
657  2 return region[0];
658    }
659  2 else if ((alPos <= region[1])
660    && (index + 1 < hiddenColumns.size()))
661    {
662    // alPos is within a hidden region, return the next one
663    // if there is one
664  1 region = hiddenColumns.get(index + 1);
665  1 return region[0];
666    }
667    }
668    }
669  7 return alPos;
670    } finally
671    {
672  13 LOCK.readLock().unlock();
673    }
674    }
675   
676    /**
677    * Answers if a column in the alignment is visible
678    *
679    * @param column
680    * absolute position of column in the alignment
681    * @return true if column is visible
682    */
 
683  3324 toggle public boolean isVisible(int column)
684    {
685  3324 try
686    {
687  3324 LOCK.readLock().lock();
688   
689  3324 if (!hiddenColumns.isEmpty())
690    {
691  64 int regionindex = cursor.findRegionForColumn(column, false)
692    .getRegionIndex();
693  64 if (regionindex > -1 && regionindex < hiddenColumns.size())
694    {
695  39 int[] region = hiddenColumns.get(regionindex);
696    // already know that column <= region[1] as cursor returns containing
697    // region or region to right
698  39 if (column >= region[0])
699    {
700  21 return false;
701    }
702    }
703    }
704  3303 return true;
705   
706    } finally
707    {
708  3324 LOCK.readLock().unlock();
709    }
710    }
711   
712    /**
713    *
714    * @return true if there are columns hidden
715    */
 
716  3280 toggle public boolean hasHiddenColumns()
717    {
718  3280 try
719    {
720  3280 LOCK.readLock().lock();
721   
722    // we don't use getSize()>0 here because it has to iterate over
723    // the full hiddenColumns collection and so will be much slower
724  3280 return (!hiddenColumns.isEmpty());
725    } finally
726    {
727  3280 LOCK.readLock().unlock();
728    }
729    }
730   
731    /**
732    *
733    * @return true if there is more than one hidden column region
734    */
 
735  10 toggle public boolean hasMultiHiddenColumnRegions()
736    {
737  10 try
738    {
739  10 LOCK.readLock().lock();
740  10 return !hiddenColumns.isEmpty() && hiddenColumns.size() > 1;
741    } finally
742    {
743  10 LOCK.readLock().unlock();
744    }
745    }
746   
747   
748    /**
749    * Returns a hashCode built from hidden column ranges
750    */
 
751  3 toggle @Override
752    public int hashCode()
753    {
754  3 try
755    {
756  3 LOCK.readLock().lock();
757  3 int hashCode = 1;
758   
759  3 for (int[] hidden : hiddenColumns)
760    {
761  5 hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
762  5 hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
763    }
764  3 return hashCode;
765    } finally
766    {
767  3 LOCK.readLock().unlock();
768    }
769    }
770   
771    /**
772    * Hide columns corresponding to the marked bits
773    *
774    * @param inserts
775    * - columns mapped to bits starting from zero
776    */
 
777  10 toggle public void hideColumns(BitSet inserts)
778    {
779  10 hideColumns(inserts, 0, inserts.length() - 1);
780    }
781   
782    /**
783    * Hide columns corresponding to the marked bits, within the range
784    * [start,end]. Entries in tohide which are outside [start,end] are ignored.
785    *
786    * @param tohide
787    * columns mapped to bits starting from zero
788    * @param start
789    * start of range to hide columns within
790    * @param end
791    * end of range to hide columns within
792    */
 
793  18 toggle private void hideColumns(BitSet tohide, int start, int end)
794    {
795  18 try
796    {
797  18 LOCK.writeLock().lock();
798  18 for (int firstSet = tohide
799  39 .nextSetBit(start), lastSet = start; firstSet >= start
800    && lastSet <= end; firstSet = tohide
801    .nextSetBit(lastSet))
802    {
803  21 lastSet = tohide.nextClearBit(firstSet);
804  21 if (lastSet <= end)
805    {
806  12 hideColumns(firstSet, lastSet - 1);
807    }
808  9 else if (firstSet <= end)
809    {
810  8 hideColumns(firstSet, end);
811    }
812    }
813  18 cursor = new HiddenColumnsCursor(hiddenColumns);
814    } finally
815    {
816  18 LOCK.writeLock().unlock();
817    }
818    }
819   
820    /**
821    * Hide columns corresponding to the marked bits, within the range
822    * [start,end]. Entries in tohide which are outside [start,end] are ignored.
823    * NB Existing entries in [start,end] are cleared.
824    *
825    * @param tohide
826    * columns mapped to bits starting from zero
827    * @param start
828    * start of range to hide columns within
829    * @param end
830    * end of range to hide columns within
831    */
 
832  8 toggle public void clearAndHideColumns(BitSet tohide, int start, int end)
833    {
834  8 clearHiddenColumnsInRange(start, end);
835  8 hideColumns(tohide, start, end);
836    }
837   
838    /**
839    * Make all columns in the range [start,end] visible
840    *
841    * @param start
842    * start of range to show columns
843    * @param end
844    * end of range to show columns
845    */
 
846  8 toggle private void clearHiddenColumnsInRange(int start, int end)
847    {
848  8 try
849    {
850  8 LOCK.writeLock().lock();
851   
852  8 if (!hiddenColumns.isEmpty())
853    {
854  7 HiddenCursorPosition pos = cursor.findRegionForColumn(start, false);
855  7 int index = pos.getRegionIndex();
856   
857  7 if (index != -1 && index != hiddenColumns.size())
858    {
859    // regionIndex is the region which either contains start
860    // or lies to the right of start
861  5 int[] region = hiddenColumns.get(index);
862  5 if (region[0] < start && region[1] >= start)
863    {
864    // region contains start, truncate so that it ends just before start
865  2 numColumns -= region[1] - start + 1;
866  2 region[1] = start - 1;
867  2 index++;
868    }
869   
870  5 int endi = index;
871  11 while (endi < hiddenColumns.size())
872    {
873  8 region = hiddenColumns.get(endi);
874   
875  8 if (region[1] > end)
876    {
877  2 if (region[0] <= end)
878    {
879    // region contains end, truncate so it starts just after end
880  1 numColumns -= end - region[0] + 1;
881  1 region[0] = end + 1;
882    }
883  2 break;
884    }
885   
886  6 numColumns -= region[1] - region[0] + 1;
887  6 endi++;
888    }
889  5 hiddenColumns.subList(index, endi).clear();
890   
891    }
892   
893  7 cursor = new HiddenColumnsCursor(hiddenColumns);
894    }
895    } finally
896    {
897  8 LOCK.writeLock().unlock();
898    }
899    }
900   
901    /**
902    *
903    * @param updates
904    * BitSet where hidden columns will be marked
905    */
 
906  2 toggle protected void andNot(BitSet updates)
907    {
908  2 try
909    {
910  2 LOCK.writeLock().lock();
911   
912  2 BitSet hiddenBitSet = new BitSet();
913  2 for (int[] range : hiddenColumns)
914    {
915  2 hiddenBitSet.set(range[0], range[1] + 1);
916    }
917  2 hiddenBitSet.andNot(updates);
918  2 hiddenColumns.clear();
919  2 hideColumns(hiddenBitSet);
920    } finally
921    {
922  2 LOCK.writeLock().unlock();
923    }
924    }
925   
926    /**
927    * Calculate the visible start and end index of an alignment.
928    *
929    * @param width
930    * full alignment width
931    * @return integer array where: int[0] = startIndex, and int[1] = endIndex
932    */
 
933  49 toggle public int[] getVisibleStartAndEndIndex(int width)
934    {
935  49 try
936    {
937  49 LOCK.readLock().lock();
938   
939  49 int firstVisible = 0;
940  49 int lastVisible = width - 1;
941   
942  49 if (!hiddenColumns.isEmpty())
943    {
944    // first visible col with index 0, convert to absolute index
945  4 firstVisible = visibleToAbsoluteColumn(0);
946   
947    // last visible column is either immediately to left of
948    // last hidden region, or is just the last column in the alignment
949  4 int[] lastregion = hiddenColumns.get(hiddenColumns.size() - 1);
950  4 if (lastregion[1] == width - 1)
951    {
952    // last region is at very end of alignment
953    // last visible column immediately precedes it
954  1 lastVisible = lastregion[0] - 1;
955    }
956    }
957  49 return new int[] { firstVisible, lastVisible };
958   
959    } finally
960    {
961  49 LOCK.readLock().unlock();
962    }
963    }
964   
965    /**
966    * Finds the hidden region (if any) which starts or ends at res
967    *
968    * @param res
969    * visible residue position, unadjusted for hidden columns
970    * @return region as [start,end] or null if no matching region is found. If
971    * res is adjacent to two regions, returns the left region.
972    */
 
973  17 toggle public int[] getRegionWithEdgeAtRes(int res)
974    {
975  17 try
976    {
977  17 LOCK.readLock().lock();
978  17 int adjres = visibleToAbsoluteColumn(res);
979   
980  17 int[] reveal = null;
981   
982  17 if (!hiddenColumns.isEmpty())
983    {
984    // look for a region ending just before adjres
985  16 int regionindex = cursor.findRegionForColumn(adjres - 1, false)
986    .getRegionIndex();
987  16 if (regionindex < hiddenColumns.size()
988    && hiddenColumns.get(regionindex)[1] == adjres - 1)
989    {
990  5 reveal = hiddenColumns.get(regionindex);
991    }
992    // check if the region ends just after adjres
993  11 else if (regionindex < hiddenColumns.size()
994    && hiddenColumns.get(regionindex)[0] == adjres + 1)
995    {
996  5 reveal = hiddenColumns.get(regionindex);
997    }
998    }
999  17 return reveal;
1000   
1001    } finally
1002    {
1003  17 LOCK.readLock().unlock();
1004    }
1005    }
1006   
1007    /**
1008    * Return an iterator over the hidden regions
1009    */
 
1010  281 toggle public Iterator<int[]> iterator()
1011    {
1012  281 try
1013    {
1014  281 LOCK.readLock().lock();
1015  281 return new RangeIterator(hiddenColumns);
1016    } finally
1017    {
1018  281 LOCK.readLock().unlock();
1019    }
1020    }
1021   
1022    /**
1023    * Return a bounded iterator over the hidden regions
1024    *
1025    * @param start
1026    * position to start from (inclusive, absolute column position)
1027    * @param end
1028    * position to end at (inclusive, absolute column position)
1029    * @return
1030    */
 
1031  55 toggle public Iterator<int[]> getBoundedIterator(int start, int end)
1032    {
1033  55 try
1034    {
1035  55 LOCK.readLock().lock();
1036  55 return new RangeIterator(start, end, hiddenColumns);
1037    } finally
1038    {
1039  55 LOCK.readLock().unlock();
1040    }
1041    }
1042   
1043    /**
1044    * Return a bounded iterator over the *visible* start positions of hidden
1045    * regions
1046    *
1047    * @param start
1048    * position to start from (inclusive, visible column position)
1049    * @param end
1050    * position to end at (inclusive, visible column position)
1051    */
 
1052  366 toggle public Iterator<Integer> getStartRegionIterator(int start, int end)
1053    {
1054  366 try
1055    {
1056  366 LOCK.readLock().lock();
1057   
1058    // get absolute position of column in alignment
1059  366 int absoluteStart = visibleToAbsoluteColumn(start);
1060   
1061    // Get cursor position and supply it to the iterator:
1062    // Since we want visible region start, we look for a cursor for the
1063    // (absoluteStart-1), then if absoluteStart is the start of a visible
1064    // region we'll get the cursor pointing to the region before, which is
1065    // what we want
1066  366 HiddenCursorPosition pos = cursor
1067    .findRegionForColumn(absoluteStart - 1, false);
1068   
1069  366 return new StartRegionIterator(pos, start, end,
1070    hiddenColumns);
1071    } finally
1072    {
1073  366 LOCK.readLock().unlock();
1074    }
1075    }
1076   
1077    /**
1078    * Return an iterator over visible *columns* (not regions) between the given
1079    * start and end boundaries
1080    *
1081    * @param start
1082    * first column (inclusive)
1083    * @param end
1084    * last column (inclusive)
1085    */
 
1086  10 toggle public Iterator<Integer> getVisibleColsIterator(int start, int end)
1087    {
1088  10 try
1089    {
1090  10 LOCK.readLock().lock();
1091  10 return new RangeElementsIterator(
1092    new VisibleContigsIterator(start, end + 1, hiddenColumns));
1093    } finally
1094    {
1095  10 LOCK.readLock().unlock();
1096    }
1097    }
1098   
1099    /**
1100    * return an iterator over visible segments between the given start and end
1101    * boundaries
1102    *
1103    * @param start
1104    * first column, inclusive from 0
1105    * @param end
1106    * last column - not inclusive
1107    * @param useVisibleCoords
1108    * if true, start and end are visible column positions, not absolute
1109    * positions*
1110    */
 
1111  770 toggle public VisibleContigsIterator getVisContigsIterator(int start,
1112    int end,
1113    boolean useVisibleCoords)
1114    {
1115  770 int adjstart = start;
1116  770 int adjend = end;
1117  770 if (useVisibleCoords)
1118    {
1119  374 adjstart = visibleToAbsoluteColumn(start);
1120  374 adjend = visibleToAbsoluteColumn(end);
1121    }
1122   
1123  770 try
1124    {
1125  770 LOCK.readLock().lock();
1126  770 return new VisibleContigsIterator(adjstart, adjend, hiddenColumns);
1127    } finally
1128    {
1129  770 LOCK.readLock().unlock();
1130    }
1131    }
1132    }