Merge branch 'develop' (JAL-4102 2.11.2.6 patch release) into features/r2_11_2_alphaf...
[jalview.git] / src / jalview / datamodel / ColumnSelection.java
index 4b1eb56..db83e76 100644 (file)
  */
 package jalview.datamodel;
 
-import jalview.util.ShiftList;
-import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
-import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
-
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
+import java.util.regex.PatternSyntaxException;
+
+import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
+import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
 
 /**
  * Data class holding the selected columns and hidden column ranges for a view.
@@ -60,7 +60,7 @@ public class ColumnSelection
      */
     IntList()
     {
-      order = new ArrayList<Integer>();
+      order = new ArrayList<>();
       _uorder = Collections.unmodifiableList(order);
       selected = new BitSet();
     }
@@ -106,7 +106,7 @@ public class ColumnSelection
     void remove(int col)
     {
 
-      Integer colInt = new Integer(col);
+      Integer colInt = Integer.valueOf(col);
 
       if (selected.get(col))
       {
@@ -204,7 +204,7 @@ public class ColumnSelection
           // clear shifted bits and update List of selected columns
           selected.clear(temp);
           mask.set(temp - change);
-          order.set(i, new Integer(temp - change));
+          order.set(i, Integer.valueOf(temp - change));
         }
       }
       // lastly update the bitfield all at once
@@ -231,7 +231,7 @@ public class ColumnSelection
      */
     List<int[]> getRanges()
     {
-      List<int[]> rlist = new ArrayList<int[]>();
+      List<int[]> rlist = new ArrayList<>();
       if (selected.isEmpty())
       {
         return rlist;
@@ -264,9 +264,7 @@ public class ColumnSelection
     }
   }
 
-  IntList selection = new IntList();
-
-  HiddenColumns hiddenColumns = new HiddenColumns();
+  private IntList selection = new IntList();
 
   /**
    * Add a column to the selection
@@ -311,7 +309,7 @@ public class ColumnSelection
     Integer colInt;
     for (int i = start; i < end; i++)
     {
-      colInt = new Integer(i);
+      colInt = Integer.valueOf(i);
       if (selection.contains(colInt))
       {
         selection.remove(colInt);
@@ -356,6 +354,22 @@ public class ColumnSelection
   }
 
   /**
+   * 
+   */
+  public boolean intersects(int from, int to)
+  {
+    // TODO: do this in a more efficient bitwise way
+    for (int f = from; f <= to; f++)
+    {
+      if (selection.isSelected(f))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * Answers true if no columns are selected, else false
    */
   public boolean isEmpty()
@@ -391,93 +405,6 @@ public class ColumnSelection
     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<int[]> 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<int[]> 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<int[]> 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)
@@ -491,14 +418,13 @@ public class ColumnSelection
 
   }
 
-
   /**
    * Hides the specified column and any adjacent selected columns
    * 
    * @param res
    *          int
    */
-  public void hideSelectedColumns(int col, AlignmentI al)
+  public void hideSelectedColumns(int col, HiddenColumns hidden)
   {
     /*
      * deselect column (whether selected or not!)
@@ -531,13 +457,9 @@ public class ColumnSelection
       min = max;
     }
 
-    al.getHiddenColumns().hideColumns(min, max);
+    hidden.hideColumns(min, max);
   }
 
-
-
-
-
   /**
    * Copy constructor
    * 
@@ -558,11 +480,6 @@ public class ColumnSelection
   {
   }
 
-
-
-
-
-
   /**
    * Invert the column selection from first to end-1. leaves hiddenColumns
    * untouched (and unselected)
@@ -572,7 +489,7 @@ public class ColumnSelection
    */
   public void invertColumnSelection(int first, int width, AlignmentI al)
   {
-    boolean hasHidden = al.getHiddenColumns().hasHidden();
+    boolean hasHidden = al.getHiddenColumns().hasHiddenColumns();
     for (int i = first; i < width; i++)
     {
       if (contains(i))
@@ -590,41 +507,28 @@ public class ColumnSelection
   }
 
   /**
-   * 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.
+   * set the selected columns to the given column selection, excluding any
+   * columns that are hidden.
    * 
    * @param colsel
    */
-  public void setElementsFrom(ColumnSelection colsel)
+  public void setElementsFrom(ColumnSelection colsel,
+          HiddenColumns hiddenColumns)
   {
     selection = new IntList();
     if (colsel.selection != null && colsel.selection.size() > 0)
     {
-      if (hiddenColumns.hasHidden())
+      if (hiddenColumns.hasHiddenColumns())
       {
         // only select visible columns in this columns selection
-        addElementsFrom(colsel);
+        for (Integer col : colsel.getSelected())
+        {
+          if (hiddenColumns != null
+                  && hiddenColumns.isVisible(col.intValue()))
+          {
+            selection.add(col);
+          }
+        }
       }
       else
       {
@@ -638,254 +542,167 @@ public class ColumnSelection
   }
 
   /**
-   * 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.
+   * @return true if there are columns marked
    */
-  public static ColumnSelection propagateInsertions(SequenceI profileseq,
-          AlignmentI al, AlignmentView input)
+  public boolean hasSelectedColumns()
   {
-    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;
+    return (selection != null && selection.size() > 0);
   }
 
   /**
+   * Selects columns where the given annotation matches the provided filter
+   * condition(s). Any existing column selections are first cleared. Answers the
+   * number of columns added.
    * 
-   * @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
+   * @param annotations
+   * @param filterParams
+   * @return
    */
-  private void propagateInsertions(SequenceI profileseq, AlignmentI al,
-          SequenceI origseq)
+  public int filterAnnotations(AlignmentAnnotation ann_row,
+          AnnotationFilterParameter filterParams)
   {
-    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())
+    Annotation[] annotations = ann_row.annotations;
+    // JBPNote - this method needs to be refactored to become independent of
+    // viewmodel package
+    this.clear();
+
+    if (ann_row.graph == AlignmentAnnotation.CUSTOMRENDERER && (filterParams
+            .getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
+            || filterParams
+                    .getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD))
     {
-      // 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++)
+      float tVal = filterParams.getThresholdValue();
+      if (ann_row.sequenceRef != null)
       {
-        SequenceI sqobj = al.getSequenceAt(s);
-        if (sqobj == profileseq)
-        {
-          continue;
-        }
-        String sq = sqobj.getSequenceAsString();
-        // pad sequence
-        int diff = origseq.getLength() - sq.length();
-        while (diff > 0)
+        // TODO - get ContactList from AlignmentView for non-seq-ref associatd
+        for (int column = 0; column < annotations.length; column++)
         {
-          // sq = sq
-          // + ((diff >= sb.length()) ? sb.toString() : sb
-          // .substring(0, diff));
-          if (diff >= sb.length())
+          if (ann_row.annotations[column] == null)
           {
-            sq += sb.toString();
+            continue;
           }
-          else
+
+          int cpos = ann_row.sequenceRef.findPosition(column) - 1;
+          ContactListI clist = ann_row.sequenceRef
+                  .getContactListFor(ann_row, cpos);
+          for (int row = column + 8,
+                  rowEnd = clist.getContactHeight(); row < rowEnd; row++)
           {
-            char[] buf = new char[diff];
-            sb.getChars(0, diff, buf, 0);
-            sq += buf.toString();
+            if (filterParams
+                    .getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
+                            ? (clist.getContactAt(row) > tVal)
+                            : (clist.getContactAt(row) < tVal))
+            {
+              addElement(column);
+              break;
+              // int column_forrowpos = ann_row.sequenceRef.findIndex(row + 1);
+              // addElement(column_forrowpos);
+            }
           }
-          diff = origseq.getLength() - sq.length();
         }
       }
+      return selection.size();
     }
-  }
-
-  /**
-   * 
-   * @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;
+    int addedCount = 0;
+    int column = 0;
     do
     {
-      if (annotations[count] != null)
+      Annotation ann = annotations[column];
+      if (ann != null)
       {
-
-        boolean itemMatched = false;
-
-        if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
-                && annotations[count].value >= filterParams
-                        .getThresholdValue())
+        float value = ann.value;
+        boolean matched = false;
+
+        /*
+         * filter may have multiple conditions - 
+         * these are or'd until a match is found
+         */
+        if (filterParams
+                .getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
+                && value > filterParams.getThresholdValue())
         {
-          itemMatched = true;
+          matched = true;
         }
-        if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD
-                && annotations[count].value <= filterParams
-                        .getThresholdValue())
+
+        if (!matched && filterParams
+                .getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD
+                && value < filterParams.getThresholdValue())
         {
-          itemMatched = true;
+          matched = true;
         }
 
-        if (filterParams.isFilterAlphaHelix()
-                && annotations[count].secondaryStructure == 'H')
+        if (!matched && filterParams.isFilterAlphaHelix()
+                && ann.secondaryStructure == 'H')
         {
-          itemMatched = true;
+          matched = true;
         }
 
-        if (filterParams.isFilterBetaSheet()
-                && annotations[count].secondaryStructure == 'E')
+        if (!matched && filterParams.isFilterBetaSheet()
+                && ann.secondaryStructure == 'E')
         {
-          itemMatched = true;
+          matched = true;
         }
 
-        if (filterParams.isFilterTurn()
-                && annotations[count].secondaryStructure == 'S')
+        if (!matched && filterParams.isFilterTurn()
+                && ann.secondaryStructure == 'S')
         {
-          itemMatched = true;
+          matched = true;
         }
 
         String regexSearchString = filterParams.getRegexString();
-        if (regexSearchString != null
-                && !filterParams.getRegexSearchFields().isEmpty())
+        if (!matched && regexSearchString != null)
         {
           List<SearchableAnnotationField> fields = filterParams
                   .getRegexSearchFields();
-          try
-          {
-            if (fields.contains(SearchableAnnotationField.DISPLAY_STRING)
-                    && annotations[count].displayCharacter
-                            .matches(regexSearchString))
-            {
-              itemMatched = true;
-            }
-          } catch (java.util.regex.PatternSyntaxException pse)
+          for (SearchableAnnotationField field : fields)
           {
-            if (annotations[count].displayCharacter
-                    .equals(regexSearchString))
+            String compareTo = field == SearchableAnnotationField.DISPLAY_STRING
+                    ? ann.displayCharacter // match 'Label'
+                    : ann.description; // and/or 'Description'
+            if (compareTo != null)
             {
-              itemMatched = true;
+              try
+              {
+                if (compareTo.matches(regexSearchString))
+                {
+                  matched = true;
+                }
+              } catch (PatternSyntaxException pse)
+              {
+                if (compareTo.equals(regexSearchString))
+                {
+                  matched = true;
+                }
+              }
+              if (matched)
+              {
+                break;
+              }
             }
           }
-          if (fields.contains(SearchableAnnotationField.DESCRIPTION)
-                  && annotations[count].description != null
-                  && annotations[count].description
-                          .matches(regexSearchString))
-          {
-            itemMatched = true;
-          }
         }
 
-        if (itemMatched)
+        if (matched)
         {
-          this.addElement(count);
+          this.addElement(column);
+          addedCount++;
         }
       }
-      count++;
-    } while (count < annotations.length);
-    return false;
+      column++;
+    } while (column < annotations.length);
+
+    return addedCount;
   }
 
   /**
-   * Returns a hashCode built from selected columns and hidden column ranges
+   * Returns a hashCode built from selected columns ranges
    */
   @Override
   public int hashCode()
   {
-    int hashCode = selection.hashCode();
-    return hiddenColumns.hashCode(hashCode);
+    return selection.hashCode();
   }
 
   /**
@@ -916,7 +733,7 @@ public class ColumnSelection
       return false;
     }
 
-    return this.hiddenColumns.equals(that.hiddenColumns);
+    return true;
   }
 
   /**
@@ -938,8 +755,8 @@ public class ColumnSelection
    * 
    * @return
    */
-  public boolean markColumns(BitSet markedColumns, int startCol,
-          int endCol, boolean invert, boolean extendCurrent, boolean toggle)
+  public boolean markColumns(BitSet markedColumns, int startCol, int endCol,
+          boolean invert, boolean extendCurrent, boolean toggle)
   {
     boolean changed = false;
     if (!extendCurrent && !toggle)