();
+ if (selected.isEmpty())
+ {
+ return rlist;
+ }
+ int next = selected.nextSetBit(0), clear = -1;
+ while (next != -1)
+ {
+ clear = selected.nextClearBit(next);
+ rlist.add(new int[] { next, clear - 1 });
+ next = selected.nextSetBit(clear);
+ }
+ return rlist;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ // TODO Auto-generated method stub
+ return selected.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof IntList)
+ {
+ return ((IntList) obj).selected.equals(selected);
+ }
+ return false;
+ }
+ }
+
+ IntList selection = new IntList();
+
+ HiddenColumns hiddenColumns = new HiddenColumns();
+
+ /**
+ * Add a column to the selection
+ *
+ * @param col
+ * index of column
+ */
+ public void addElement(int col)
+ {
+ selection.add(col);
+ }
+
+ /**
+ * clears column selection
+ */
+ public void clear()
+ {
+ selection.clear();
+ }
+
+ /**
+ * Removes value 'col' from the selection (not the col'th item)
+ *
+ * @param col
+ * index of column to be removed
+ */
+ public void removeElement(int col)
+ {
+ selection.remove(col);
+ }
+
+ /**
+ * removes a range of columns from the selection
+ *
+ * @param start
+ * int - first column in range to be removed
+ * @param end
+ * int - last col
+ */
+ public void removeElements(int start, int end)
+ {
+ Integer colInt;
+ for (int i = start; i < end; i++)
+ {
+ colInt = new Integer(i);
+ if (selection.contains(colInt))
+ {
+ selection.remove(colInt);
+ }
+ }
+ }
+
+ /**
+ * Returns a read-only view of the (possibly empty) list of selected columns
+ *
+ * The list contains no duplicates but is not necessarily ordered. It also may
+ * include columns hidden from the current view. To modify (for example sort)
+ * the list, you should first make a copy.
+ *
+ * The list is not thread-safe: iterating over it could result in
+ * ConcurrentModificationException if it is modified by another thread.
+ */
+ public List getSelected()
+ {
+ return selection.getList();
+ }
+
+ /**
+ * @return list of int arrays containing start and end column position for
+ * runs of selected columns ordered from right to left.
+ */
+ public List getSelectedRanges()
+ {
+ return selection.getRanges();
+ }
+
+ /**
+ *
+ * @param col
+ * index to search for in column selection
+ *
+ * @return true if col is selected
+ */
+ public boolean contains(int col)
+ {
+ return (col > -1) ? selection.isSelected(col) : false;
+ }
+
+ /**
+ * Answers true if no columns are selected, else false
+ */
+ public boolean isEmpty()
+ {
+ return selection == null || selection.isEmpty();
+ }
+
+ /**
+ * rightmost selected column
+ *
+ * @return rightmost column in alignment that is selected
+ */
+ public int getMax()
+ {
+ if (selection.isEmpty())
+ {
+ return -1;
+ }
+ return selection.getMaxColumn();
+ }
+
+ /**
+ * Leftmost column in selection
+ *
+ * @return column index of leftmost column in selection
+ */
+ public int getMin()
+ {
+ if (selection.isEmpty())
+ {
+ return 1000000000;
+ }
+ return selection.getMinColumn();
+ }
+
+ /**
+ * propagate shift in alignment columns to column selection
+ *
+ * @param start
+ * beginning of edit
+ * @param left
+ * shift in edit (+ve for removal, or -ve for inserts)
+ */
+ /* public List compensateForEdit(int start, int change)
+ {
+ selection.compensateForEdits(start, change);
+ return hiddenColumns.compensateForEdit(start, change, this);
+ }
+ */
+ /**
+ * 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)
+ */
+ private void compensateForDelEdits(int start, int change)
+ {
+ selection.compensateForEdits(start, change);
+ hiddenColumns.compensateForDelEdits(start, change);
+ }
+
+ /**
+ * Adjust hidden column boundaries based on a series of column additions or
+ * deletions in visible regions.
+ *
+ * @param shiftrecord
+ * @return
+ */
+ private ShiftList compensateForEdits(ShiftList shiftrecord)
+ {
+ if (shiftrecord != null)
+ {
+ final List shifts = shiftrecord.getShifts();
+ if (shifts != null && shifts.size() > 0)
+ {
+ int shifted = 0;
+ for (int i = 0, j = shifts.size(); i < j; i++)
+ {
+ int[] sh = shifts.get(i);
+ compensateForDelEdits(shifted + sh[0], sh[1]);
+ shifted -= sh[1];
+ }
+ }
+ return shiftrecord.getInverse();
+ }
+ return null;
+ }
+
+
+ /**
+ * 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)
+ {
+ hiddenColumns.pruneDeletions(shifts);
+
+ if (selection != null && selection.size() > 0)
+ {
+ selection.pruneColumnList(shifts);
+ if (selection != null && selection.size() == 0)
+ {
+ selection = null;
+ }
+ }
+ // and shift the rest.
+ this.compensateForEdits(deletions);
+ }
+ }
+ }
+
+
+ public void hideSelectedColumns(AlignmentI al)
+ {
+ synchronized (selection)
+ {
+ for (int[] selregions : selection.getRanges())
+ {
+ al.getHiddenColumns().hideColumns(selregions[0], selregions[1]);
+ }
+ selection.clear();
+ }
+
+ }
+
+
+ /**
+ * Hides the specified column and any adjacent selected columns
+ *
+ * @param res
+ * int
+ */
+ public void hideSelectedColumns(int col, HiddenColumns hidden)
+ {
+ /*
+ * deselect column (whether selected or not!)
+ */
+ removeElement(col);
+
+ /*
+ * find adjacent selected columns
+ */
+ int min = col - 1, max = col + 1;
+ while (contains(min))
+ {
+ removeElement(min);
+ min--;
+ }
+
+ while (contains(max))
+ {
+ removeElement(max);
+ max++;
+ }
+
+ /*
+ * min, max are now the closest unselected columns
+ */
+ min++;
+ max--;
+ if (min > max)
+ {
+ min = max;
+ }
+
+ hidden.hideColumns(min, max);
+ }
+
+
+
+
+
+ /**
+ * Copy constructor
+ *
+ * @param copy
+ */
+ public ColumnSelection(ColumnSelection copy)
+ {
+ if (copy != null)
+ {
+ selection = new IntList(copy.selection);
+ }
+ }
+
+ /**
+ * ColumnSelection
+ */
+ public ColumnSelection()
+ {
+ }
+
+
+
+
+
+
+ /**
+ * Invert the column selection from first to end-1. leaves hiddenColumns
+ * untouched (and unselected)
+ *
+ * @param first
+ * @param end
+ */
+ public void invertColumnSelection(int first, int width, AlignmentI al)
+ {
+ boolean hasHidden = al.getHiddenColumns().hasHidden();
+ for (int i = first; i < width; i++)
+ {
+ if (contains(i))
+ {
+ removeElement(i);
+ }
+ else
+ {
+ if (!hasHidden || al.getHiddenColumns().isVisible(i))
+ {
+ addElement(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * add in any unselected columns from the given column selection, excluding
+ * any that are hidden.
+ *
+ * @param colsel
+ */
+ public void addElementsFrom(ColumnSelection colsel)
+ {
+ if (colsel != null && !colsel.isEmpty())
+ {
+ for (Integer col : colsel.getSelected())
+ {
+ if (hiddenColumns != null
+ && hiddenColumns.isVisible(col.intValue()))
+ {
+ selection.add(col);
+ }
+ }
+ }
+ }
+
+ /**
+ * set the selected columns the given column selection, excluding any columns
+ * that are hidden.
+ *
+ * @param colsel
+ */
+ public void setElementsFrom(ColumnSelection colsel)
+ {
+ selection = new IntList();
+ if (colsel.selection != null && colsel.selection.size() > 0)
+ {
+ if (hiddenColumns.hasHidden())
+ {
+ // only select visible columns in this columns selection
+ addElementsFrom(colsel);
+ }
+ else
+ {
+ // add everything regardless
+ for (Integer col : colsel.getSelected())
+ {
+ addElement(col);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add gaps into the sequences aligned to profileseq under the given
+ * AlignmentView
+ *
+ * @param profileseq
+ * @param al
+ * - alignment to have gaps inserted into it
+ * @param input
+ * - alignment view where sequence corresponding to profileseq is
+ * first entry
+ * @return new Column selection for new alignment view, with insertions into
+ * profileseq marked as hidden.
+ */
+ public static ColumnSelection propagateInsertions(SequenceI profileseq,
+ AlignmentI al, AlignmentView input)
+ {
+ int profsqpos = 0;
+
+ // return propagateInsertions(profileseq, al, )
+ char gc = al.getGapCharacter();
+ Object[] alandcolsel = input.getAlignmentAndHiddenColumns(gc);
+ ColumnSelection nview = (ColumnSelection) alandcolsel[1];
+ SequenceI origseq = ((SequenceI[]) alandcolsel[0])[profsqpos];
+ nview.propagateInsertions(profileseq, al, origseq);
+ return nview;
+ }
+
+ /**
+ *
+ * @param profileseq
+ * - sequence in al which corresponds to origseq
+ * @param al
+ * - alignment which is to have gaps inserted into it
+ * @param origseq
+ * - sequence corresponding to profileseq which defines gap map for
+ * modifying al
+ */
+ private void propagateInsertions(SequenceI profileseq, AlignmentI al,
+ 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 = hiddenColumns.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)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++)
+ {
+ sb.append(gc);
+ }
+ 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.
+ StringBuffer sb = new StringBuffer();
+ for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; 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();
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * @return true if there are columns marked
+ */
+ public boolean hasSelectedColumns()
+ {
+ return (selection != null && selection.size() > 0);
+ }
+
+
+
+ public boolean filterAnnotations(Annotation[] annotations,
+ AnnotationFilterParameter filterParams)
+ {
+ // JBPNote - this method needs to be refactored to become independent of
+ // viewmodel package
+ hiddenColumns.revealAllHiddenColumns(this);
+ this.clear();
+ int count = 0;
+ do
+ {
+ if (annotations[count] != null)
+ {
+
+ boolean itemMatched = false;
+
+ if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
+ && annotations[count].value >= filterParams
+ .getThresholdValue())
+ {
+ itemMatched = true;
+ }
+ if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD
+ && annotations[count].value <= filterParams
+ .getThresholdValue())
+ {
+ itemMatched = true;
+ }
+
+ if (filterParams.isFilterAlphaHelix()
+ && annotations[count].secondaryStructure == 'H')
+ {
+ itemMatched = true;
+ }
+
+ if (filterParams.isFilterBetaSheet()
+ && annotations[count].secondaryStructure == 'E')
+ {
+ itemMatched = true;
+ }
+
+ if (filterParams.isFilterTurn()
+ && annotations[count].secondaryStructure == 'S')
+ {
+ itemMatched = true;
+ }
+
+ String regexSearchString = filterParams.getRegexString();
+ if (regexSearchString != null
+ && !filterParams.getRegexSearchFields().isEmpty())
+ {
+ List fields = filterParams
+ .getRegexSearchFields();
+ try
+ {
+ if (fields.contains(SearchableAnnotationField.DISPLAY_STRING)
+ && annotations[count].displayCharacter
+ .matches(regexSearchString))
+ {
+ itemMatched = true;
+ }
+ } catch (java.util.regex.PatternSyntaxException pse)
+ {
+ if (annotations[count].displayCharacter
+ .equals(regexSearchString))
+ {
+ itemMatched = true;
+ }
+ }
+ if (fields.contains(SearchableAnnotationField.DESCRIPTION)
+ && annotations[count].description != null
+ && annotations[count].description
+ .matches(regexSearchString))
+ {
+ itemMatched = true;
+ }
+ }
+
+ if (itemMatched)
+ {
+ this.addElement(count);
+ }
+ }
+ count++;
+ } while (count < annotations.length);
+ return false;
+ }
+
+ /**
+ * Returns a hashCode built from selected columns and hidden column ranges
+ */
+ @Override
+ public int hashCode()
+ {
+ int hashCode = selection.hashCode();
+ return hiddenColumns.hashCode(hashCode);
+ }
+
+ /**
+ * Answers true if comparing to a ColumnSelection with the same selected
+ * columns and hidden columns, else false
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof ColumnSelection))
+ {
+ return false;
+ }
+ ColumnSelection that = (ColumnSelection) obj;
+
+ /*
+ * check columns selected are either both null, or match
+ */
+ if (this.selection == null)
+ {
+ if (that.selection != null)
+ {
+ return false;
+ }
+ }
+ if (!this.selection.equals(that.selection))
+ {
+ return false;
+ }
+
+ return this.hiddenColumns.equals(that.hiddenColumns);
+ }
+
+ /**
+ * Updates the column selection depending on the parameters, and returns true
+ * if any change was made to the selection
+ *
+ * @param markedColumns
+ * a set identifying marked columns (base 0)
+ * @param startCol
+ * the first column of the range to operate over (base 0)
+ * @param endCol
+ * the last column of the range to operate over (base 0)
+ * @param invert
+ * if true, deselect marked columns and select unmarked
+ * @param extendCurrent
+ * if true, extend rather than replacing the current column selection
+ * @param toggle
+ * if true, toggle the selection state of marked columns
+ *
+ * @return
+ */
+ public boolean markColumns(BitSet markedColumns, int startCol,
+ int endCol, boolean invert, boolean extendCurrent, boolean toggle)
+ {
+ boolean changed = false;
+ if (!extendCurrent && !toggle)
+ {
+ changed = !this.isEmpty();
+ clear();
+ }
+ if (invert)
+ {
+ // invert only in the currently selected sequence region
+ int i = markedColumns.nextClearBit(startCol);
+ int ibs = markedColumns.nextSetBit(startCol);
+ while (i >= startCol && i <= endCol)
+ {
+ if (ibs < 0 || i < ibs)
+ {
+ changed = true;
+ if (toggle && contains(i))
+ {
+ removeElement(i++);
+ }
+ else
+ {
+ addElement(i++);
+ }
+ }
+ else
+ {
+ i = markedColumns.nextClearBit(ibs);
+ ibs = markedColumns.nextSetBit(i);
+ }
+ }
+ }
+ else
+ {
+ int i = markedColumns.nextSetBit(startCol);
+ while (i >= startCol && i <= endCol)
+ {
+ changed = true;
+ if (toggle && contains(i))
+ {
+ removeElement(i);
+ }
+ else
+ {
+ addElement(i);
+ }
+ i = markedColumns.nextSetBit(i + 1);
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Adjusts column selections, and the given selection group, to match the
+ * range of a stretch (e.g. mouse drag) operation
+ *
+ * Method refactored from ScalePanel.mouseDragged
+ *
+ * @param res
+ * current column position, adjusted for hidden columns
+ * @param sg
+ * current selection group
+ * @param min
+ * start position of the stretch group
+ * @param max
+ * end position of the stretch group
+ */
+ public void stretchGroup(int res, SequenceGroup sg, int min, int max)
+ {
+ if (!contains(res))
+ {
+ addElement(res);
+ }
+
+ if (res > sg.getStartRes())
+ {
+ // expand selection group to the right
+ sg.setEndRes(res);
+ }
+ if (res < sg.getStartRes())
+ {
+ // expand selection group to the left
+ sg.setStartRes(res);
+ }
+
+ /*
+ * expand or shrink column selection to match the
+ * range of the drag operation
+ */
+ for (int col = min; col <= max; col++)
+ {
+ if (col < sg.getStartRes() || col > sg.getEndRes())
+ {
+ // shrinking drag - remove from selection
+ removeElement(col);
+ }
+ else
+ {
+ // expanding drag - add to selection
+ addElement(col);
+ }
+ }
+ }
+}