X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fdatamodel%2FHiddenColumns.java;h=c77fb7b5e6fa56c1bd6eb118063d40dce3cecc40;hb=14aeb3c39e60a604bdeed33949ba05e0c8c8be5d;hp=9722c0a1b12a7bab5685a5e488583f97af91cf74;hpb=5e8ec77d921ff2d604811e5e4ba7e9211b0f48de;p=jalview.git
diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java
index 9722c0a..c77fb7b 100644
--- a/src/jalview/datamodel/HiddenColumns.java
+++ b/src/jalview/datamodel/HiddenColumns.java
@@ -1,38 +1,168 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see .
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel;
import jalview.util.Comparison;
-import jalview.util.ShiftList;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-public class HiddenColumns implements Iterable
+public class HiddenColumns
{
- private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
-
+ private static final int HASH_MULTIPLIER = 31;
+
+ private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
+
/*
* list of hidden column [start, end] ranges; the list is maintained in
* ascending start column order
*/
- private Vector hiddenColumns;
+ private ArrayList hiddenColumns;
+
+ /**
+ * Constructor
+ */
+ public HiddenColumns()
+ {
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param copy
+ */
+ public HiddenColumns(HiddenColumns copy)
+ {
+ try
+ {
+ LOCK.writeLock().lock();
+ if (copy != null)
+ {
+ if (copy.hiddenColumns != null)
+ {
+ hiddenColumns = copy.copyHiddenRegionsToArrayList(0);
+ }
+ }
+ } finally
+ {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Copy constructor within bounds and with offset. Copies hidden column
+ * regions fully contained between start and end, and offsets positions by
+ * subtracting offset.
+ *
+ * @param copy
+ * HiddenColumns instance to copy from
+ * @param start
+ * lower bound to copy from
+ * @param end
+ * upper bound to copy to
+ * @param offset
+ * offset to subtract from each region boundary position
+ *
+ */
+ public HiddenColumns(HiddenColumns copy, int start, int end, int offset)
+ {
+ try
+ {
+ LOCK.writeLock().lock();
+ if (copy != null)
+ {
+ hiddenColumns = new ArrayList<>();
+ Iterator it = copy.getBoundedIterator(start, end);
+ while (it.hasNext())
+ {
+ int[] region = it.next();
+ // still need to check boundaries because iterator returns
+ // all overlapping regions and we need contained regions
+ if (region[0] >= start && region[1] <= end)
+ {
+ hiddenColumns.add(
+ new int[]
+ { region[0] - offset, region[1] - offset });
+ }
+ }
+ }
+ } finally
+ {
+ LOCK.writeLock().unlock();
+ }
+ }
/**
- * This Method is used to return all the HiddenColumn regions
+ * This method is used to return all the HiddenColumn regions and is intended
+ * to remain private. External callers which need a copy of the regions can
+ * call getHiddenColumnsCopyAsList.
*
* @return empty list or List of hidden column intervals
*/
- public List getHiddenRegions()
+ private List getHiddenRegions()
{
return hiddenColumns == null ? Collections. emptyList()
: hiddenColumns;
}
/**
+ * Output regions data as a string. String is in the format:
+ * reg0[0]reg0[1]reg1[0]reg1[1] ... regn[1]
+ *
+ * @param delimiter
+ * string to delimit regions
+ * @param betweenstring
+ * to put between start and end region values
+ * @return regions formatted according to delimiter and between strings
+ */
+ public String regionsToString(String delimiter, String between)
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ StringBuilder regionBuilder = new StringBuilder();
+ if (hiddenColumns != null)
+ {
+ for (int[] range : hiddenColumns)
+ {
+ regionBuilder.append(delimiter).append(range[0]).append(between)
+ .append(range[1]);
+ }
+
+ regionBuilder.deleteCharAt(0);
+ }
+ return regionBuilder.toString();
+ } finally
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+
+ /**
* Find the number of hidden columns
*
* @return number of hidden columns
@@ -41,9 +171,9 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
int size = 0;
- if (hasHidden())
+ if (hasHiddenColumns())
{
for (int[] range : hiddenColumns)
{
@@ -51,29 +181,32 @@ public class HiddenColumns implements Iterable
}
}
return size;
- }
- finally
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
/**
- * Answers if there are any hidden columns
+ * Get the number of distinct hidden regions
*
- * @return true if there are hidden columns
+ * @return number of regions
*/
- public boolean hasHidden()
+ public int getNumberOfRegions()
{
try
{
- lock.readLock().lock();
- return (hiddenColumns != null) && (!hiddenColumns.isEmpty());
+ LOCK.readLock().lock();
+ int num = 0;
+ if (hasHiddenColumns())
+ {
+ num = hiddenColumns.size();
+ }
+ return num;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
-
}
@Override
@@ -81,7 +214,7 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
if (!(obj instanceof HiddenColumns))
{
@@ -113,7 +246,7 @@ public class HiddenColumns implements Iterable
return true;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -128,13 +261,13 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
int result = column;
if (hiddenColumns != null)
{
for (int i = 0; i < hiddenColumns.size(); i++)
{
- int[] region = hiddenColumns.elementAt(i);
+ int[] region = hiddenColumns.get(i);
if (result >= region[0])
{
result += region[1] - region[0] + 1;
@@ -144,7 +277,7 @@ public class HiddenColumns implements Iterable
return result;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -161,7 +294,7 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
int result = hiddenColumn;
if (hiddenColumns != null)
{
@@ -169,7 +302,7 @@ public class HiddenColumns implements Iterable
int[] region;
do
{
- region = hiddenColumns.elementAt(index++);
+ region = hiddenColumns.get(index++);
if (hiddenColumn > region[1])
{
result -= region[1] + 1 - region[0];
@@ -203,7 +336,7 @@ public class HiddenColumns implements Iterable
// columns.
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -223,93 +356,108 @@ public class HiddenColumns implements Iterable
try
{
- lock.readLock().lock();
- int distance = visibleDistance;
+ LOCK.readLock().lock();
+ int distance = visibleDistance;
- // in case startColumn is in a hidden region, move it to the left
- int start = adjustForHiddenColumns(findColumnPosition(startColumn));
+ // in case startColumn is in a hidden region, move it to the left
+ int start = adjustForHiddenColumns(findColumnPosition(startColumn));
- // get index of hidden region to left of start
- int index = getHiddenIndexLeft(start);
- if (index == -1)
- {
- // no hidden regions to left of startColumn
- return start - distance;
- }
+ // get index of hidden region to left of start
+ int index = getHiddenIndexLeft(start);
+ if (index == -1)
+ {
+ // no hidden regions to left of startColumn
+ return start - distance;
+ }
- // walk backwards through the alignment subtracting the counts of visible
- // columns from distance
- int[] region;
- int gap = 0;
- int nextstart = start;
+ // walk backwards through the alignment subtracting the counts of visible
+ // columns from distance
+ int[] region;
+ int gap = 0;
+ int nextstart = start;
- while ((index > -1) && (distance - gap > 0))
- {
- // subtract the gap to right of region from distance
- distance -= gap;
- start = nextstart;
+ while ((index > -1) && (distance - gap > 0))
+ {
+ // subtract the gap to right of region from distance
+ distance -= gap;
+ start = nextstart;
- // calculate the next gap
- region = hiddenColumns.get(index);
- gap = start - region[1];
+ // calculate the next gap
+ region = hiddenColumns.get(index);
+ gap = start - region[1];
- // set start to just to left of current region
- nextstart = region[0] - 1;
- index--;
- }
+ // set start to just to left of current region
+ nextstart = region[0] - 1;
+ index--;
+ }
- if (distance - gap > 0)
- {
- // fell out of loop because there are no more hidden regions
- distance -= gap;
- return nextstart - distance;
- }
- return start - distance;
+ if (distance - gap > 0)
+ {
+ // fell out of loop because there are no more hidden regions
+ distance -= gap;
+ return nextstart - distance;
+ }
+ return start - distance;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
/**
- * Use this method to determine where the next hiddenRegion starts
+ * Use this method to determine the set of hiddenRegion start positions
+ * between absolute position and absolute position
*
- * @param hiddenRegion
- * index of hidden region (counts from 0)
- * @return column number in visible view
+ * @param start
+ * absolute residue to start from
+ * @param end
+ * absolute residue to end at
+ *
+ * @return list of column numbers in *visible* view where hidden regions start
*/
- public int findHiddenRegionPosition(int hiddenRegion)
+ public List findHiddenRegionPositions(int start, int end)
{
try
{
- lock.readLock().lock();
- int result = 0;
+ LOCK.readLock().lock();
+ List positions = null;
+
if (hiddenColumns != null)
{
- int index = 0;
- int gaps = 0;
- do
- {
- int[] region = hiddenColumns.elementAt(index);
- if (hiddenRegion == 0)
- {
- return region[0];
- }
+ positions = new ArrayList<>(hiddenColumns.size());
- gaps += region[1] + 1 - region[0];
- result = region[1] + 1;
- index++;
- } while (index <= hiddenRegion);
+ // navigate to start, keeping count of hidden columns
+ int i = 0;
+ int hiddenSoFar = 0;
+ while ((i < hiddenColumns.size())
+ && (hiddenColumns.get(i)[0] < start))
+ {
+ int[] region = hiddenColumns.get(i);
+ hiddenSoFar += region[1] - region[0] + 1;
+ i++;
+ }
- result -= gaps;
+ // iterate from start to end, adding start positions of each
+ // hidden region. Positions are visible columns count, not absolute
+ while (i < hiddenColumns.size()
+ && (hiddenColumns.get(i)[0] < end))
+ {
+ int[] region = hiddenColumns.get(i);
+ positions.add(region[0] - hiddenSoFar);
+ hiddenSoFar += region[1] - region[0] + 1;
+ i++;
+ }
+ }
+ else
+ {
+ positions = new ArrayList<>();
}
- return result;
- }
- finally
+ return positions;
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -324,13 +472,13 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
if (hiddenColumns != null)
{
int index = 0;
do
{
- int[] region = hiddenColumns.elementAt(index);
+ int[] region = hiddenColumns.get(index);
if (alPos < region[0])
{
return region[0];
@@ -343,7 +491,7 @@ public class HiddenColumns implements Iterable
return alPos;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -359,27 +507,27 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
- if (hiddenColumns != null)
- {
- int index = hiddenColumns.size() - 1;
- do
+ if (hiddenColumns != null)
{
- int[] region = hiddenColumns.elementAt(index);
- if (alPos > region[1])
+ int index = hiddenColumns.size() - 1;
+ do
{
- return region[1];
- }
+ int[] region = hiddenColumns.get(index);
+ if (alPos > region[1])
+ {
+ return region[1];
+ }
- index--;
- } while (index > -1);
- }
+ index--;
+ } while (index > -1);
+ }
- return alPos;
+ return alPos;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -396,26 +544,26 @@ public class HiddenColumns implements Iterable
try
{
- lock.readLock().lock();
- if (hiddenColumns != null)
- {
- int index = hiddenColumns.size() - 1;
- do
+ LOCK.readLock().lock();
+ if (hiddenColumns != null)
{
- int[] region = hiddenColumns.elementAt(index);
- if (pos > region[1])
+ int index = hiddenColumns.size() - 1;
+ do
{
- return index;
- }
+ int[] region = hiddenColumns.get(index);
+ if (pos > region[1])
+ {
+ return index;
+ }
- index--;
- } while (index > -1);
- }
+ index--;
+ } while (index > -1);
+ }
- return -1;
+ return -1;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -428,96 +576,91 @@ public class HiddenColumns implements Iterable
*/
public void hideColumns(int start, int end)
{
- hideColumns(start, end, false);
- }
-
- /**
- * Adds the specified column range to the hidden columns
- *
- * @param start
- * @param end
- */
- private void hideColumns(int start, int end, boolean alreadyLocked)
- {
+ boolean wasAlreadyLocked = false;
try
{
-
- if (!alreadyLocked)
+ // check if the write lock was already locked by this thread,
+ // as this method can be called internally in loops within HiddenColumns
+ if (!LOCK.isWriteLockedByCurrentThread())
{
- lock.writeLock().lock();
+ LOCK.writeLock().lock();
}
-
- if (hiddenColumns == null)
- {
- hiddenColumns = new Vector<>();
- }
-
- /*
- * traverse existing hidden ranges and insert / amend / append as
- * appropriate
- */
- for (int i = 0; i < hiddenColumns.size(); i++)
- {
- int[] region = hiddenColumns.elementAt(i);
-
- if (end < region[0] - 1)
+ else
{
- /*
- * insert discontiguous preceding range
- */
- hiddenColumns.insertElementAt(new int[] { start, end }, i);
- return;
+ wasAlreadyLocked = true;
}
- if (end <= region[1])
+ if (hiddenColumns == null)
{
- /*
- * new range overlaps existing, or is contiguous preceding it - adjust
- * start column
- */
- region[0] = Math.min(region[0], start);
- return;
+ hiddenColumns = new ArrayList<>();
}
- if (start <= region[1] + 1)
+ /*
+ * traverse existing hidden ranges and insert / amend / append as
+ * appropriate
+ */
+ for (int i = 0; i < hiddenColumns.size(); i++)
{
- /*
- * new range overlaps existing, or is contiguous following it - adjust
- * start and end columns
- */
- region[0] = Math.min(region[0], start);
- region[1] = Math.max(region[1], end);
+ int[] region = hiddenColumns.get(i);
+
+ if (end < region[0] - 1)
+ {
+ /*
+ * insert discontiguous preceding range
+ */
+ hiddenColumns.add(i, new int[] { start, end });
+ return;
+ }
- /*
- * also update or remove any subsequent ranges
- * that are overlapped
- */
- while (i < hiddenColumns.size() - 1)
+ if (end <= region[1])
{
- int[] nextRegion = hiddenColumns.get(i + 1);
- if (nextRegion[0] > end + 1)
+ /*
+ * new range overlaps existing, or is contiguous preceding it - adjust
+ * start column
+ */
+ region[0] = Math.min(region[0], start);
+ return;
+ }
+
+ if (start <= region[1] + 1)
+ {
+ /*
+ * new range overlaps existing, or is contiguous following it - adjust
+ * start and end columns
+ */
+ region[0] = Math.min(region[0], start);
+ region[1] = Math.max(region[1], end);
+
+ /*
+ * also update or remove any subsequent ranges
+ * that are overlapped
+ */
+ while (i < hiddenColumns.size() - 1)
{
- /*
- * gap to next hidden range - no more to update
- */
- break;
+ int[] nextRegion = hiddenColumns.get(i + 1);
+ if (nextRegion[0] > end + 1)
+ {
+ /*
+ * gap to next hidden range - no more to update
+ */
+ break;
+ }
+ region[1] = Math.max(nextRegion[1], end);
+ hiddenColumns.remove(i + 1);
}
- region[1] = Math.max(nextRegion[1], end);
- hiddenColumns.remove(i + 1);
+ return;
}
- return;
}
- }
- /*
- * remaining case is that the new range follows everything else
- */
- hiddenColumns.addElement(new int[] { start, end });
+ /*
+ * remaining case is that the new range follows everything else
+ */
+ hiddenColumns.add(new int[] { start, end });
} finally
{
- if (!alreadyLocked)
+ if (!wasAlreadyLocked)
{
- lock.writeLock().unlock();
+ LOCK.writeLock().unlock();
}
}
}
@@ -526,82 +669,40 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
- if (hiddenColumns != null)
- {
- for (int[] region : hiddenColumns)
+ if (hiddenColumns != null)
{
- if (column >= region[0] && column <= region[1])
+ for (int[] region : hiddenColumns)
{
- return false;
+ if (column >= region[0] && column <= region[1])
+ {
+ return false;
+ }
}
}
- }
- return true;
+ return true;
} finally
{
- lock.readLock().unlock();
- }
- }
-
- /**
- * ColumnSelection
- */
- public HiddenColumns()
- {
- }
-
- /**
- * Copy constructor
- *
- * @param copy
- */
- public HiddenColumns(HiddenColumns copy)
- {
- try
- {
-
- lock.readLock().lock();
- if (copy != null)
- {
- if (copy.hiddenColumns != null)
- {
- hiddenColumns = copy.copyHiddenRegions();
- }
- }
- }
- finally
- {
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
- private Vector copyHiddenRegions()
+ private ArrayList copyHiddenRegionsToArrayList(int startIndex)
{
- Vector copy = new Vector<>(hiddenColumns.size());
- for (int i = 0, j = hiddenColumns.size(); i < j; i++)
+ int size = 0;
+ if (hiddenColumns != null)
{
- int[] rh, cp;
- rh = hiddenColumns.elementAt(i);
- if (rh != null)
- {
- cp = new int[rh.length];
- System.arraycopy(rh, 0, cp, 0, rh.length);
- copy.addElement(cp);
- }
+ size = hiddenColumns.size();
}
- return copy;
- }
+ ArrayList copy = new ArrayList<>(size);
- private ArrayList copyHiddenRegionsToArrayList()
- {
- ArrayList copy = new ArrayList<>(hiddenColumns.size());
- for (int i = 0, j = hiddenColumns.size(); i < j; i++)
+ for (int i = startIndex, j = size; i < j; i++)
{
- int[] rh, cp;
- rh = hiddenColumns.elementAt(i);
+ int[] rh;
+ int[] cp;
+ rh = hiddenColumns.get(i);
if (rh != null)
{
cp = new int[rh.length];
@@ -609,202 +710,71 @@ public class HiddenColumns implements Iterable
copy.add(cp);
}
}
- return copy;
- }
-
- public void getHiddenColumnsCopy(Vector copy)
- {
- try
- {
- lock.readLock().lock();
- copy = copyHiddenRegions();
- } finally
- {
- lock.readLock().unlock();
- }
- }
- public void getHiddenColumnsCopy(ArrayList copy)
- {
- try
- {
- lock.readLock().lock();
- copy = copyHiddenRegionsToArrayList();
- } finally
- {
- lock.readLock().unlock();
- }
+ return copy;
}
/**
- * propagate shift in alignment columns to column selection
+ * return all visible segments between the given start and end boundaries
*
* @param start
- * beginning of edit
- * @param left
- * shift in edit (+ve for removal, or -ve for inserts)
+ * (first column inclusive from 0)
+ * @param end
+ * (last column - not inclusive)
+ * @return List {[i_start, i_end], ..} where intervals lie in
+ * start<=i_start<=i_end compensateForEdit(int start, int change,
- ColumnSelection sel)
+ public List getVisibleContigs(int start, int end)
{
try
{
- lock.writeLock().lock();
- List deletedHiddenColumns = null;
-
- if (hiddenColumns != null)
+ LOCK.readLock().lock();
+ List vcontigs = new ArrayList<>();
+ if (hiddenColumns != null && hiddenColumns.size() > 0)
{
- deletedHiddenColumns = new ArrayList<>();
- int hSize = hiddenColumns.size();
- for (int i = 0; i < hSize; i++)
+ int vstart = start;
+ int hideStart;
+ int hideEnd;
+
+ for (int[] region : hiddenColumns)
{
- int[] region = hiddenColumns.elementAt(i);
- if (region[0] > start && start + change > region[1])
- {
- deletedHiddenColumns.add(region);
+ hideStart = region[0];
+ hideEnd = region[1];
- hiddenColumns.removeElementAt(i);
- i--;
- hSize--;
+ // navigate to start
+ if (hideEnd < vstart)
+ {
continue;
}
-
- if (region[0] > start)
+ if (hideStart > vstart)
{
- region[0] -= change;
- region[1] -= change;
+ int[] contig = new int[] { vstart, hideStart - 1 };
+ vcontigs.add(contig);
}
+ vstart = hideEnd + 1;
- if (region[0] < 0)
+ // exit if we're past the end
+ if (vstart >= end)
{
- region[0] = 0;
+ break;
}
-
- }
-
- this.revealHiddenColumns(0, sel);
- }
-
- return deletedHiddenColumns;
- } finally
- {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * propagate shift in alignment columns to column selection special version of
- * compensateForEdit - allowing for edits within hidden regions
- *
- * @param start
- * beginning of edit
- * @param left
- * shift in edit (+ve for removal, or -ve for inserts)
- */
- public void compensateForDelEdits(int start, int change)
- {
- try
- {
- lock.writeLock().lock();
- if (hiddenColumns != null)
- {
- for (int i = 0; i < hiddenColumns.size(); i++)
- {
- int[] region = hiddenColumns.elementAt(i);
- if (region[0] >= start)
- {
- region[0] -= change;
- }
- if (region[1] >= start)
- {
- region[1] -= change;
- }
- if (region[1] < region[0])
- {
- hiddenColumns.removeElementAt(i--);
- }
-
- if (region[0] < 0)
- {
- region[0] = 0;
- }
- if (region[1] < 0)
- {
- region[1] = 0;
- }
- }
- }
- }
- finally
- {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * return all visible segments between the given start and end boundaries
- *
- * @param start
- * (first column inclusive from 0)
- * @param end
- * (last column - not inclusive)
- * @return int[] {i_start, i_end, ..} where intervals lie in
- * start<=i_start<=i_end 0)
- {
- List visiblecontigs = new ArrayList<>();
- List regions = getHiddenRegions();
-
- int vstart = start;
- int[] region;
- int hideStart, hideEnd;
-
- for (int j = 0; vstart < end && j < regions.size(); j++)
- {
- region = regions.get(j);
- hideStart = region[0];
- hideEnd = region[1];
-
- if (hideEnd < vstart)
- {
- continue;
- }
- if (hideStart > vstart)
- {
- visiblecontigs.add(new int[] { vstart, hideStart - 1 });
- }
- vstart = hideEnd + 1;
}
if (vstart < end)
{
- visiblecontigs.add(new int[] { vstart, end - 1 });
+ int[] contig = new int[] { vstart, end - 1 };
+ vcontigs.add(contig);
}
- int[] vcontigs = new int[visiblecontigs.size() * 2];
- for (int i = 0, j = visiblecontigs.size(); i < j; i++)
- {
- int[] vc = visiblecontigs.get(i);
- visiblecontigs.set(i, null);
- vcontigs[i * 2] = vc[0];
- vcontigs[i * 2 + 1] = vc[1];
- }
- visiblecontigs.clear();
- return vcontigs;
}
else
{
- return new int[] { start, end - 1 };
+ int[] contig = new int[] { start, end - 1 };
+ vcontigs.add(contig);
}
- }
- finally
+ return vcontigs;
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -813,23 +783,22 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
- int i, iSize = seqs.length;
- String selections[] = new String[iSize];
+ LOCK.readLock().lock();
+ int iSize = seqs.length;
+ String[] selections = new String[iSize];
if (hiddenColumns != null && hiddenColumns.size() > 0)
{
- for (i = 0; i < iSize; i++)
+ for (int i = 0; i < iSize; i++)
{
StringBuffer visibleSeq = new StringBuffer();
- List regions = getHiddenRegions();
- int blockStart = start, blockEnd = end;
- int[] region;
- int hideStart, hideEnd;
+ int blockStart = start;
+ int blockEnd = end;
+ int hideStart;
+ int hideEnd;
- for (int j = 0; j < regions.size(); j++)
+ for (int[] region : hiddenColumns)
{
- region = regions.get(j);
hideStart = region[0];
hideEnd = region[1];
@@ -862,17 +831,16 @@ public class HiddenColumns implements Iterable
}
else
{
- for (i = 0; i < iSize; i++)
+ for (int i = 0; i < iSize; i++)
{
selections[i] = seqs[i].getSequenceAsString(start, end);
}
}
return selections;
- }
- finally
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -890,23 +858,29 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
- int fpos = seq.getStart(), lpos = seq.getEnd();
+ LOCK.readLock().lock();
+ int fpos = seq.getStart();
+ int lpos = seq.getEnd();
int start = 0;
if (hiddenColumns == null || hiddenColumns.size() == 0)
{
- int ifpos = seq.findIndex(fpos) - 1,
- ilpos = seq.findIndex(lpos) - 1;
- return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos };
+ int ifpos = seq.findIndex(fpos) - 1;
+ int ilpos = seq.findIndex(lpos) - 1;
+ return new int[] { ifpos, ifpos, ilpos };
}
// Simply walk along the sequence whilst watching for hidden column
// boundaries
List regions = getHiddenRegions();
- int spos = fpos, lastvispos = -1, rcount = 0,
- hideStart = seq.getLength(), hideEnd = -1;
- int visPrev = 0, visNext = 0, firstP = -1, lastP = -1;
+ int spos = fpos;
+ int rcount = 0;
+ int hideStart = seq.getLength();
+ int hideEnd = -1;
+ int visPrev = 0;
+ int visNext = 0;
+ int firstP = -1;
+ int lastP = -1;
boolean foundStart = false;
for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
&& p < pLen; p++)
@@ -942,7 +916,6 @@ public class HiddenColumns implements Iterable
start = p;
foundStart = true;
}
- lastvispos = p;
lpos = spos;
}
// look for next sequence position
@@ -951,15 +924,13 @@ public class HiddenColumns implements Iterable
}
if (foundStart)
{
- return new int[] { findColumnPosition(start),
- findColumnPosition(lastvispos), fpos, lpos, firstP, lastP };
+ return new int[] { findColumnPosition(start), firstP, lastP };
}
// otherwise, sequence was completely hidden
- return new int[] { visPrev, visNext, 0, 0, firstP, lastP };
- }
- finally
+ return new int[] { visPrev, firstP, lastP };
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -990,7 +961,7 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
if (alignmentAnnotation.annotations == null)
{
return;
@@ -1005,14 +976,14 @@ public class HiddenColumns implements Iterable
// then mangle the alignmentAnnotation annotation array
Vector annels = new Vector<>();
Annotation[] els = null;
- List regions = getHiddenRegions();
- int blockStart = start, blockEnd = end;
- int[] region;
- int hideStart, hideEnd, w = 0;
+ int blockStart = start;
+ int blockEnd = end;
+ int hideStart;
+ int hideEnd;
+ int w = 0;
- for (int j = 0; j < regions.size(); j++)
+ for (int[] region : hiddenColumns)
{
- region = regions.get(j);
hideStart = region[0];
hideEnd = region[1];
@@ -1029,7 +1000,8 @@ public class HiddenColumns implements Iterable
break;
}
- annels.addElement(els = new Annotation[blockEnd - blockStart]);
+ els = new Annotation[blockEnd - blockStart];
+ annels.addElement(els);
System.arraycopy(alignmentAnnotation.annotations, blockStart, els,
0, els.length);
w += els.length;
@@ -1039,7 +1011,8 @@ public class HiddenColumns implements Iterable
if (end > blockStart)
{
- annels.addElement(els = new Annotation[end - blockStart + 1]);
+ els = new Annotation[end - blockStart + 1];
+ annels.addElement(els);
if ((els.length
+ blockStart) <= alignmentAnnotation.annotations.length)
{
@@ -1075,10 +1048,9 @@ public class HiddenColumns implements Iterable
{
alignmentAnnotation.restrict(start, end);
}
- }
- finally
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -1090,11 +1062,11 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
return hiddenColumns != null && hiddenColumns.size() > 0;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -1106,11 +1078,11 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
return hiddenColumns != null && hiddenColumns.size() > 1;
} finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -1124,15 +1096,15 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.writeLock().lock();
+ LOCK.writeLock().lock();
List inserts = sr.getInsertions();
for (int[] r : inserts)
{
- hideColumns(r[0], r[1], true);
+ hideColumns(r[0], r[1]);
}
} finally
{
- lock.writeLock().unlock();
+ LOCK.writeLock().unlock();
}
}
@@ -1143,12 +1115,12 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.writeLock().lock();
+ LOCK.writeLock().lock();
if (hiddenColumns != null)
{
for (int i = 0; i < hiddenColumns.size(); i++)
{
- int[] region = hiddenColumns.elementAt(i);
+ int[] region = hiddenColumns.get(i);
for (int j = region[0]; j < region[1] + 1; j++)
{
sel.addElement(j);
@@ -1157,10 +1129,9 @@ public class HiddenColumns implements Iterable
}
hiddenColumns = null;
- }
- finally
+ } finally
{
- lock.writeLock().unlock();
+ LOCK.writeLock().unlock();
}
}
@@ -1174,10 +1145,10 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.writeLock().lock();
+ LOCK.writeLock().lock();
for (int i = 0; i < hiddenColumns.size(); i++)
{
- int[] region = hiddenColumns.elementAt(i);
+ int[] region = hiddenColumns.get(i);
if (start == region[0])
{
for (int j = region[0]; j < region[1] + 1; j++)
@@ -1185,7 +1156,7 @@ public class HiddenColumns implements Iterable
sel.addElement(j);
}
- hiddenColumns.removeElement(region);
+ hiddenColumns.remove(region);
break;
}
}
@@ -1193,132 +1164,9 @@ public class HiddenColumns implements Iterable
{
hiddenColumns = null;
}
- }
- finally
- {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * removes intersection of position,length ranges in deletions from the
- * start,end regions marked in intervals.
- *
- * @param shifts
- * @param intervals
- * @return
- */
- private boolean pruneIntervalVector(final List shifts,
- Vector intervals)
- {
- boolean pruned = false;
- int i = 0, j = intervals.size() - 1, s = 0, t = shifts.size() - 1;
- int hr[] = intervals.elementAt(i);
- int sr[] = shifts.get(s);
- while (i <= j && s <= t)
- {
- boolean trailinghn = hr[1] >= sr[0];
- if (!trailinghn)
- {
- if (i < j)
- {
- hr = intervals.elementAt(++i);
- }
- else
- {
- i++;
- }
- continue;
- }
- int endshift = sr[0] + sr[1]; // deletion ranges - -ve means an insert
- if (endshift < hr[0] || endshift < sr[0])
- { // leadinghc disjoint or not a deletion
- if (s < t)
- {
- sr = shifts.get(++s);
- }
- else
- {
- s++;
- }
- continue;
- }
- boolean leadinghn = hr[0] >= sr[0];
- boolean leadinghc = hr[0] < endshift;
- boolean trailinghc = hr[1] < endshift;
- if (leadinghn)
- {
- if (trailinghc)
- { // deleted hidden region.
- intervals.removeElementAt(i);
- pruned = true;
- j--;
- if (i <= j)
- {
- hr = intervals.elementAt(i);
- }
- continue;
- }
- if (leadinghc)
- {
- hr[0] = endshift; // clip c terminal region
- leadinghn = !leadinghn;
- pruned = true;
- }
- }
- if (!leadinghn)
- {
- if (trailinghc)
- {
- if (trailinghn)
- {
- hr[1] = sr[0] - 1;
- pruned = true;
- }
- }
- else
- {
- // sr contained in hr
- if (s < t)
- {
- sr = shifts.get(++s);
- }
- else
- {
- s++;
- }
- continue;
- }
- }
- }
- return pruned; // true if any interval was removed or modified by
- // operations.
- }
-
- /**
- * remove any hiddenColumns or selected columns and shift remaining based on a
- * series of position, range deletions.
- *
- * @param deletions
- */
- public void pruneDeletions(List shifts)
- {
- try
- {
- lock.writeLock().lock();
- // delete any intervals intersecting.
- if (hiddenColumns != null)
- {
- pruneIntervalVector(shifts, hiddenColumns);
- if (hiddenColumns != null && hiddenColumns.size() == 0)
- {
- hiddenColumns = null;
- }
- }
- }
- finally
+ } finally
{
- lock.writeLock().unlock();
+ LOCK.writeLock().unlock();
}
}
@@ -1362,154 +1210,114 @@ public class HiddenColumns implements Iterable
SequenceI origseq)
{
char gc = al.getGapCharacter();
- // recover mapping between sequence's non-gap positions and positions
- // mapping to view.
- pruneDeletions(ShiftList.parseMap(origseq.gapMap()));
- int[] viscontigs = al.getHiddenColumns().getVisibleContigs(0,
- profileseq.getLength());
- int spos = 0;
- int offset = 0;
- // add profile to visible contigs
- for (int v = 0; v < viscontigs.length; v += 2)
- {
- if (viscontigs[v] > spos)
+ // take the set of hidden columns, and the set of gaps in origseq,
+ // and remove all the hidden gaps from hiddenColumns
+
+ // first get the gaps as a Bitset
+ BitSet gaps = origseq.gapBitset();
+
+ // now calculate hidden ^ not(gap)
+ BitSet hidden = new BitSet();
+ markHiddenRegions(hidden);
+ hidden.andNot(gaps);
+ hiddenColumns = null;
+ this.hideMarkedBits(hidden);
+
+ // for each sequence in the alignment, except the profile sequence,
+ // insert gaps corresponding to each hidden region
+ // but where each hidden column region is shifted backwards by the number of
+ // preceding visible gaps
+ // update hidden columns at the same time
+ Iterator regions = iterator();
+ ArrayList newhidden = new ArrayList<>();
+
+ int numGapsBefore = 0;
+ int gapPosition = 0;
+ while (regions.hasNext())
+ {
+ // get region coordinates accounting for gaps
+ // we can rely on gaps not being *in* hidden regions because we already
+ // removed those
+ int[] region = regions.next();
+ while (gapPosition < region[0])
{
- StringBuffer sb = new StringBuffer();
- for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)
+ gapPosition++;
+ if (gaps.get(gapPosition))
{
- sb.append(gc);
+ numGapsBefore++;
}
- for (int s = 0, ns = al.getHeight(); s < ns; s++)
- {
- SequenceI sqobj = al.getSequenceAt(s);
- if (sqobj != profileseq)
- {
- String sq = al.getSequenceAt(s).getSequenceAsString();
- if (sq.length() <= spos + offset)
- {
- // pad sequence
- int diff = spos + offset - sq.length() - 1;
- if (diff > 0)
- {
- // pad gaps
- sq = sq + sb;
- while ((diff = spos + offset - sq.length() - 1) > 0)
- {
- // sq = sq
- // + ((diff >= sb.length()) ? sb.toString() : sb
- // .substring(0, diff));
- if (diff >= sb.length())
- {
- sq += sb.toString();
- }
- else
- {
- char[] buf = new char[diff];
- sb.getChars(0, diff, buf, 0);
- sq += buf.toString();
- }
- }
- }
- sq += sb.toString();
- }
- else
- {
- al.getSequenceAt(s).setSequence(
- sq.substring(0, spos + offset) + sb.toString()
- + sq.substring(spos + offset));
- }
- }
- }
- // offset+=sb.length();
}
- spos = viscontigs[v + 1] + 1;
- }
- if ((offset + spos) < profileseq.getLength())
- {
- // pad the final region with gaps.
+
+ int left = region[0] - numGapsBefore;
+ int right = region[1] - numGapsBefore;
+ newhidden.add(new int[] { left, right });
+
+ // make a string with number of gaps = length of hidden region
StringBuffer sb = new StringBuffer();
- for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++)
+ for (int s = 0; s < right - left + 1; s++)
{
sb.append(gc);
}
- for (int s = 0, ns = al.getHeight(); s < ns; s++)
- {
- SequenceI sqobj = al.getSequenceAt(s);
- if (sqobj == profileseq)
- {
- continue;
- }
- String sq = sqobj.getSequenceAsString();
- // pad sequence
- int diff = origseq.getLength() - sq.length();
- while (diff > 0)
- {
- // sq = sq
- // + ((diff >= sb.length()) ? sb.toString() : sb
- // .substring(0, diff));
- if (diff >= sb.length())
- {
- sq += sb.toString();
- }
- else
- {
- char[] buf = new char[diff];
- sb.getChars(0, diff, buf, 0);
- sq += buf.toString();
- }
- diff = origseq.getLength() - sq.length();
- }
- }
- }
- }
+ padGaps(sb, left, profileseq, al);
- /**
- * remove any hiddenColumns or selected columns and shift remaining based on a
- * series of position, range deletions.
- *
- * @param deletions
- */
- private void pruneDeletions(ShiftList deletions)
- {
- if (deletions != null)
- {
- final List shifts = deletions.getShifts();
- if (shifts != null && shifts.size() > 0)
- {
- pruneDeletions(shifts);
-
- // and shift the rest.
- this.compensateForEdits(deletions);
- }
}
+ hiddenColumns = newhidden;
}
/**
- * Adjust hidden column boundaries based on a series of column additions or
- * deletions in visible regions.
+ * Pad gaps in all sequences in alignment except profileseq
*
- * @param shiftrecord
- * @return
+ * @param sb
+ * gap string to insert
+ * @param left
+ * position to insert at
+ * @param profileseq
+ * sequence not to pad
+ * @param al
+ * alignment to pad sequences in
*/
- private ShiftList compensateForEdits(ShiftList shiftrecord)
+ private void padGaps(StringBuffer sb, int pos, SequenceI profileseq,
+ AlignmentI al)
{
- if (shiftrecord != null)
+ // loop over the sequences and pad with gaps where required
+ for (int s = 0, ns = al.getHeight(); s < ns; s++)
{
- final List shifts = shiftrecord.getShifts();
- if (shifts != null && shifts.size() > 0)
+ SequenceI sqobj = al.getSequenceAt(s);
+ if (sqobj != profileseq)
{
- int shifted = 0;
- for (int i = 0, j = shifts.size(); i < j; i++)
+ String sq = al.getSequenceAt(s).getSequenceAsString();
+ if (sq.length() <= pos)
+ {
+ // pad sequence
+ int diff = pos - sq.length() - 1;
+ if (diff > 0)
+ {
+ // pad gaps
+ sq = sq + sb;
+ while ((diff = pos - sq.length() - 1) > 0)
+ {
+ if (diff >= sb.length())
+ {
+ sq += sb.toString();
+ }
+ else
+ {
+ char[] buf = new char[diff];
+ sb.getChars(0, diff, buf, 0);
+ sq += buf.toString();
+ }
+ }
+ }
+ sq += sb.toString();
+ }
+ else
{
- int[] sh = shifts.get(i);
- compensateForDelEdits(shifted + sh[0], sh[1]);
- shifted -= sh[1];
+ al.getSequenceAt(s).setSequence(
+ sq.substring(0, pos) + sb.toString() + sq.substring(pos));
}
}
- return shiftrecord.getInverse();
}
- return null;
}
/**
@@ -1520,21 +1328,20 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
int hashCode = 1;
if (hiddenColumns != null)
{
for (int[] hidden : hiddenColumns)
{
- hashCode = 31 * hashCode + hidden[0];
- hashCode = 31 * hashCode + hidden[1];
+ hashCode = HASH_MULTIPLIER * hashCode + hidden[0];
+ hashCode = HASH_MULTIPLIER * hashCode + hidden[1];
}
}
return hashCode;
- }
- finally
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
}
}
@@ -1548,17 +1355,17 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.writeLock().lock();
+ LOCK.writeLock().lock();
for (int firstSet = inserts
.nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts
.nextSetBit(lastSet))
{
lastSet = inserts.nextClearBit(firstSet);
- hideColumns(firstSet, lastSet - 1, true);
+ hideColumns(firstSet, lastSet - 1);
}
} finally
{
- lock.writeLock().unlock();
+ LOCK.writeLock().unlock();
}
}
@@ -1571,7 +1378,7 @@ public class HiddenColumns implements Iterable
{
try
{
- lock.readLock().lock();
+ LOCK.readLock().lock();
if (hiddenColumns == null)
{
return;
@@ -1580,21 +1387,455 @@ public class HiddenColumns implements Iterable
{
inserts.set(range[0], range[1] + 1);
}
+ } finally
+ {
+ LOCK.readLock().unlock();
}
- finally
+ }
+
+ /**
+ * Calculate the visible start and end index of an alignment.
+ *
+ * @param width
+ * full alignment width
+ * @return integer array where: int[0] = startIndex, and int[1] = endIndex
+ */
+ public int[] getVisibleStartAndEndIndex(int width)
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ int[] alignmentStartEnd = new int[] { 0, width - 1 };
+ int startPos = alignmentStartEnd[0];
+ int endPos = alignmentStartEnd[1];
+
+ int[] lowestRange = new int[] { -1, -1 };
+ int[] higestRange = new int[] { -1, -1 };
+
+ if (hiddenColumns == null)
+ {
+ return new int[] { startPos, endPos };
+ }
+
+ for (int[] hiddenCol : hiddenColumns)
+ {
+ lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+ higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+ }
+
+ if (lowestRange[0] == -1 && lowestRange[1] == -1)
+ {
+ startPos = alignmentStartEnd[0];
+ }
+ else
+ {
+ startPos = lowestRange[1] + 1;
+ }
+
+ if (higestRange[0] == -1 && higestRange[1] == -1)
+ {
+ endPos = alignmentStartEnd[1];
+ }
+ else
+ {
+ endPos = higestRange[0] - 1;
+ }
+ return new int[] { startPos, endPos };
+ } finally
{
- lock.readLock().unlock();
+ LOCK.readLock().unlock();
+ }
+
+ }
+
+ /**
+ * Finds the hidden region (if any) which starts or ends at res
+ *
+ * @param res
+ * visible residue position, unadjusted for hidden columns
+ * @return region as [start,end] or null if no matching region is found
+ */
+ public int[] getRegionWithEdgeAtRes(int res)
+ {
+ try
+ {
+ LOCK.readLock().lock();
+ int adjres = adjustForHiddenColumns(res);
+
+ int[] reveal = null;
+ if (hiddenColumns != null)
+ {
+ for (int[] region : hiddenColumns)
+ {
+ if (adjres + 1 == region[0] || adjres - 1 == region[1])
+ {
+ reveal = region;
+ break;
+ }
+ }
+ }
+ return reveal;
+ } finally
+ {
+ LOCK.readLock().unlock();
}
}
- @Override
public Iterator iterator()
{
- if (hiddenColumns == null)
+ if (hiddenColumns != null)
{
- return Collections. emptyList().iterator();
+ int last = hiddenColumns.get(hiddenColumns.size() - 1)[1];
+ return new BoundedHiddenColsIterator(0, last, true);
+ }
+ else
+ {
+ return new BoundedHiddenColsIterator(0, 0, true);
}
- return hiddenColumns.iterator();
}
+ public Iterator getBoundedIterator(int start, int end)
+ {
+ return new BoundedHiddenColsIterator(start, end, true);
+ }
+
+ public Iterator getBoundedStartIterator(int start, int end)
+ {
+ return new BoundedStartRegionIterator(start, end, true);
+ }
+
+ public Iterator getVisibleColsIterator(int start, int end)
+ {
+ return new VisibleColsIterator(start, end, true);
+ }
+
+ /**
+ * An iterator which iterates over hidden column regions in a range.
+ *
+ * @author kmourao
+ *
+ */
+ class BoundedHiddenColsIterator implements Iterator
+ {
+ private int start; // start position to iterate from
+
+ private int end; // end position to iterate to
+
+ // current index in hiddenColumns
+ private int currentPosition = 0;
+
+ // current column in hiddenColumns
+ private int[] currentRegion;
+
+ // whether to make a local copy of hiddenColumns
+ private final boolean useCopy;
+
+ // local copy or reference to hiddenColumns
+ private List localHidden;
+
+ /**
+ * Construct an iterator over hiddenColums bounded at
+ * [lowerBound,upperBound]
+ *
+ * @param lowerBound
+ * lower bound to iterate from
+ * @param upperBound
+ * upper bound to iterate to
+ * @param useCopyCols
+ * whether to make a local copy of hiddenColumns for iteration (set
+ * to true if calling from outwith the HiddenColumns class)
+ */
+ BoundedHiddenColsIterator(int lowerBound, int upperBound,
+ boolean useCopyCols)
+ {
+ start = lowerBound;
+ end = upperBound;
+ useCopy = useCopyCols;
+
+ try
+ {
+ if (useCopy)
+ {
+ // assume that if useCopy is false the calling code has locked
+ // hiddenColumns
+ LOCK.readLock().lock();
+ }
+
+ if (hiddenColumns != null)
+ {
+ localHidden = new ArrayList<>();
+
+ // iterate until a region overlaps with [start,end]
+ int i = 0;
+ while ((i < hiddenColumns.size())
+ && (hiddenColumns.get(i)[1] < start))
+ {
+ i++;
+ }
+
+ // iterate from start to end, adding each hidden region. Positions are
+ // absolute, and all regions which *overlap* [start,end] are added.
+ while (i < hiddenColumns.size()
+ && (hiddenColumns.get(i)[0] <= end))
+ {
+ int[] rh;
+ int[] cp;
+ rh = hiddenColumns.get(i);
+ if (rh != null)
+ {
+ cp = new int[rh.length];
+ System.arraycopy(rh, 0, cp, 0, rh.length);
+ localHidden.add(cp);
+ }
+ i++;
+ }
+ }
+ }
+ finally
+ {
+ if (useCopy)
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return (localHidden != null)
+ && (currentPosition < localHidden.size());
+ }
+
+ @Override
+ public int[] next()
+ {
+ currentRegion = localHidden.get(currentPosition);
+ currentPosition++;
+ return currentRegion;
+ }
+ }
+
+ class BoundedStartRegionIterator implements Iterator
+ {
+
+ private int start; // start position to iterate from
+
+ private int end; // end position to iterate to
+
+ // current index in hiddenColumns
+ private int currentPosition = 0;
+
+ // local copy or reference to hiddenColumns
+ private List positions = null;
+
+ /**
+ * Construct an iterator over hiddenColums bounded at
+ * [lowerBound,upperBound]
+ *
+ * @param lowerBound
+ * lower bound to iterate from
+ * @param upperBound
+ * upper bound to iterate to
+ * @param useCopyCols
+ * whether to make a local copy of hiddenColumns for iteration (set
+ * to true if calling from outwith the HiddenColumns class)
+ */
+ BoundedStartRegionIterator(int lowerBound, int upperBound,
+ boolean useCopy)
+ {
+ start = lowerBound;
+ end = upperBound;
+
+ try
+ {
+ if (useCopy)
+ {
+ // assume that if useCopy is false the calling code has locked
+ // hiddenColumns
+ LOCK.readLock().lock();
+ }
+
+ if (hiddenColumns != null)
+ {
+ positions = new ArrayList<>(hiddenColumns.size());
+
+ // navigate to start, keeping count of hidden columns
+ int i = 0;
+ int hiddenSoFar = 0;
+ while ((i < hiddenColumns.size())
+ && (hiddenColumns.get(i)[0] < start + hiddenSoFar))
+ {
+ int[] region = hiddenColumns.get(i);
+ hiddenSoFar += region[1] - region[0] + 1;
+ i++;
+ }
+
+ // iterate from start to end, adding start positions of each
+ // hidden region. Positions are visible columns count, not absolute
+ while (i < hiddenColumns.size()
+ && (hiddenColumns.get(i)[0] <= end + hiddenSoFar))
+ {
+ int[] region = hiddenColumns.get(i);
+ positions.add(region[0] - hiddenSoFar);
+ hiddenSoFar += region[1] - region[0] + 1;
+ i++;
+ }
+ }
+ else
+ {
+ positions = new ArrayList<>();
+ }
+ } finally
+ {
+ if (useCopy)
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return (currentPosition < positions.size());
+ }
+
+ @Override
+ public Integer next()
+ {
+ int result = positions.get(currentPosition);
+ currentPosition++;
+ return result;
+ }
+ }
+
+ public class VisibleColsIterator implements Iterator
+ {
+ private int last;
+
+ private int current;
+
+ private int next;
+
+ private List localHidden = new ArrayList<>();
+
+ private int lasthiddenregion;
+
+ public VisibleColsIterator(int firstcol, int lastcol, boolean useCopy)
+ {
+ last = lastcol;
+ current = firstcol;
+ next = firstcol;
+ lasthiddenregion = -1;
+
+ try
+ {
+ if (useCopy)
+ {
+ // assume that if useCopy is false the calling code has locked
+ // hiddenColumns
+ LOCK.readLock().lock();
+ }
+
+ if (hiddenColumns != null)
+ {
+ int i = 0;
+ for (i = 0; i < hiddenColumns.size(); ++i)
+ {
+ if (current >= hiddenColumns.get(i)[0]
+ && current <= hiddenColumns.get(i)[1])
+ {
+ // current is hidden, move to right
+ current = hiddenColumns.get(i)[1] + 1;
+ next = current;
+ }
+ if (current < hiddenColumns.get(i)[0])
+ {
+ break;
+ }
+ }
+
+ lasthiddenregion = i - 1;
+
+ for (i = hiddenColumns.size() - 1; i >= 0; --i)
+ {
+ if (last >= hiddenColumns.get(i)[0]
+ && last <= hiddenColumns.get(i)[1])
+ {
+ // last is hidden, move to left
+ last = hiddenColumns.get(i)[0] - 1;
+ }
+ if (last > hiddenColumns.get(i)[1])
+ {
+ break;
+ }
+ }
+
+ // make a local copy of the bit we need
+ i = lasthiddenregion + 1;
+ while (i < hiddenColumns.size()
+ && hiddenColumns.get(i)[0] <= last)
+ {
+ int[] region = new int[] { hiddenColumns.get(i)[0],
+ hiddenColumns.get(i)[1] };
+ localHidden.add(region);
+ i++;
+ }
+ lasthiddenregion = -1;
+ }
+ } finally
+ {
+ if (useCopy)
+ {
+ LOCK.readLock().unlock();
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return next <= last;
+ }
+
+ @Override
+ public Integer next()
+ {
+ if (next > last)
+ {
+ throw new NoSuchElementException();
+ }
+ current = next;
+ if ((localHidden != null)
+ && (lasthiddenregion + 1 < localHidden.size()))
+ {
+ // still some more hidden regions
+ if (next + 1 < localHidden.get(lasthiddenregion + 1)[0])
+ {
+ // next+1 is still before the next hidden region
+ next++;
+ }
+ else if ((next + 1 >= localHidden.get(lasthiddenregion + 1)[0])
+ && (next + 1 <= localHidden.get(lasthiddenregion + 1)[1]))
+ {
+ // next + 1 is in the next hidden region
+ next = localHidden.get(lasthiddenregion + 1)[1] + 1;
+ lasthiddenregion++;
+ }
+ }
+ else
+ {
+ // finished with hidden regions, just increment normally
+ next++;
+ }
+ return current;
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
}