JAL-3053
[jalview.git] / src / jalview / datamodel / AlignmentAnnotation.java
index ffc3105..ee9389c 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.analysis.SecStrConsensus.SimpleBP;
 import jalview.analysis.WUSSParseException;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -164,6 +165,64 @@ public class AlignmentAnnotation
   }
 
   /**
+   * Get the RNA Secondary Structure SequenceFeature Array if present
+   */
+  public SequenceFeature[] getRnaSecondaryStructure()
+  {
+    return this._rnasecstr;
+  }
+
+  /**
+   * Check the RNA Secondary Structure is equivalent to one in given
+   * AlignmentAnnotation param
+   */
+  public boolean rnaSecondaryStructureEquivalent(AlignmentAnnotation that)
+  {
+    return rnaSecondaryStructureEquivalent(that, true);
+  }
+
+  public boolean rnaSecondaryStructureEquivalent(AlignmentAnnotation that, boolean compareType)
+  {
+    SequenceFeature[] thisSfArray = this.getRnaSecondaryStructure();
+    SequenceFeature[] thatSfArray = that.getRnaSecondaryStructure();
+    if (thisSfArray == null || thatSfArray == null)
+    {
+      return thisSfArray == null && thatSfArray == null;
+    }
+    if (thisSfArray.length != thatSfArray.length)
+    {
+      return false;
+    }
+    Arrays.sort(thisSfArray, new SFSortByEnd()); // probably already sorted
+                                                   // like this
+    Arrays.sort(thatSfArray, new SFSortByEnd()); // probably already sorted
+                                                   // like this
+    for (int i=0; i < thisSfArray.length; i++) {
+      SequenceFeature thisSf = thisSfArray[i];
+      SequenceFeature thatSf = thatSfArray[i];
+      if (compareType) {
+        if (thisSf.getType() == null || thatSf.getType() == null) {
+          if (thisSf.getType() == null && thatSf.getType() == null) {
+            continue;
+          } else {
+            return false;
+          }
+        }
+        if (! thisSf.getType().equals(thatSf.getType())) {
+          return false;
+        }
+      }
+      if (!(thisSf.getBegin() == thatSf.getBegin()
+              && thisSf.getEnd() == thatSf.getEnd()))
+      {
+        return false;
+      }
+    }
+    return true;
+
+  }
+
+  /**
    * map of positions in the associated annotation
    */
   private Map<Integer, Annotation> sequenceMapping;
@@ -294,6 +353,7 @@ public class AlignmentAnnotation
     char firstChar = 0;
     for (int i = 0; i < annotations.length; i++)
     {
+      // DEBUG System.out.println(i + ": " + annotations[i]);
       if (annotations[i] == null)
       {
         continue;
@@ -301,12 +361,15 @@ public class AlignmentAnnotation
       if (annotations[i].secondaryStructure == 'H'
               || annotations[i].secondaryStructure == 'E')
       {
+        // DEBUG System.out.println( "/H|E/ '" +
+        // annotations[i].secondaryStructure + "'");
         hasIcons |= true;
       }
       else
       // Check for RNA secondary structure
       {
-        // System.out.println(annotations[i].secondaryStructure);
+        // DEBUG System.out.println( "/else/ '" +
+        // annotations[i].secondaryStructure + "'");
         // TODO: 2.8.2 should this ss symbol validation check be a function in
         // RNA/ResidueProperties ?
         if (annotations[i].secondaryStructure == '('
@@ -317,10 +380,12 @@ public class AlignmentAnnotation
                 || annotations[i].secondaryStructure == 'B'
                 || annotations[i].secondaryStructure == 'C'
                 || annotations[i].secondaryStructure == 'D'
-                || annotations[i].secondaryStructure == 'E'
+                // || annotations[i].secondaryStructure == 'E' // ambiguous on
+                // its own -- already checked above
                 || annotations[i].secondaryStructure == 'F'
                 || annotations[i].secondaryStructure == 'G'
-                || annotations[i].secondaryStructure == 'H'
+                // || annotations[i].secondaryStructure == 'H' // ambiguous on
+                // its own -- already checked above
                 || annotations[i].secondaryStructure == 'I'
                 || annotations[i].secondaryStructure == 'J'
                 || annotations[i].secondaryStructure == 'K'
@@ -367,7 +432,7 @@ public class AlignmentAnnotation
         // &&
         // annotations[i].displayCharacter.charAt(0)==annotations[i].secondaryStructure
                 firstChar != ' ' && firstChar != '$' && firstChar != 0xCE
-                && firstChar != '(' && firstChar != '[' && firstChar != '>'
+                && firstChar != '(' && firstChar != '[' && firstChar != '<'
                 && firstChar != '{' && firstChar != 'A' && firstChar != 'B'
                 && firstChar != 'C' && firstChar != 'D' && firstChar != 'E'
                 && firstChar != 'F' && firstChar != 'G' && firstChar != 'H'
@@ -666,7 +731,7 @@ public class AlignmentAnnotation
     this.calcId = annotation.calcId;
     if (annotation.properties != null)
     {
-      properties = new HashMap<String, String>();
+      properties = new HashMap<>();
       for (Map.Entry<String, String> val : annotation.properties.entrySet())
       {
         properties.put(val.getKey(), val.getValue());
@@ -702,7 +767,7 @@ public class AlignmentAnnotation
       if (annotation.sequenceMapping != null)
       {
         Integer p = null;
-        sequenceMapping = new HashMap<Integer, Annotation>();
+        sequenceMapping = new HashMap<>();
         Iterator<Integer> pos = annotation.sequenceMapping.keySet()
                 .iterator();
         while (pos.hasNext())
@@ -782,7 +847,7 @@ public class AlignmentAnnotation
       int epos = sequenceRef.findPosition(endRes);
       if (sequenceMapping != null)
       {
-        Map<Integer, Annotation> newmapping = new HashMap<Integer, Annotation>();
+        Map<Integer, Annotation> newmapping = new HashMap<>();
         Iterator<Integer> e = sequenceMapping.keySet().iterator();
         while (e.hasNext())
         {
@@ -909,7 +974,7 @@ public class AlignmentAnnotation
     {
       return;
     }
-    sequenceMapping = new HashMap<Integer, Annotation>();
+    sequenceMapping = new HashMap<>();
 
     int seqPos;
 
@@ -1124,7 +1189,7 @@ public class AlignmentAnnotation
     {
       return;
     }
-    hidden.makeVisibleAnnotation(this);
+    makeVisibleAnnotation(hidden);
   }
 
   public void setPadGaps(boolean padgaps, char gapchar)
@@ -1190,7 +1255,7 @@ public class AlignmentAnnotation
   /**
    * properties associated with the calcId
    */
-  protected Map<String, String> properties = new HashMap<String, String>();
+  protected Map<String, String> properties = new HashMap<>();
 
   /**
    * base colour for line graphs. If null, will be set automatically by
@@ -1236,7 +1301,7 @@ public class AlignmentAnnotation
             : false;
 
     // TODO build a better annotation element map and get rid of annotations[]
-    Map<Integer, Annotation> mapForsq = new HashMap<Integer, Annotation>();
+    Map<Integer, Annotation> mapForsq = new HashMap<>();
     if (sequenceMapping != null)
     {
       if (sp2sq != null)
@@ -1289,7 +1354,7 @@ public class AlignmentAnnotation
     if (mapping != null)
     {
       Map<Integer, Annotation> old = sequenceMapping;
-      Map<Integer, Annotation> remap = new HashMap<Integer, Annotation>();
+      Map<Integer, Annotation> remap = new HashMap<>();
       int index = -1;
       for (int mp[] : mapping.values())
       {
@@ -1347,7 +1412,7 @@ public class AlignmentAnnotation
   {
     if (properties == null)
     {
-      properties = new HashMap<String, String>();
+      properties = new HashMap<>();
     }
     properties.put(property, value);
   }
@@ -1473,6 +1538,115 @@ public class AlignmentAnnotation
     return graphMin < graphMax;
   }
 
+  /**
+   * delete any columns in alignmentAnnotation that are hidden (including
+   * sequence associated annotation).
+   * 
+   * @param hiddenColumns
+   *          the set of hidden columns
+   */
+  public void makeVisibleAnnotation(HiddenColumns hiddenColumns)
+  {
+    if (annotations != null)
+    {
+      makeVisibleAnnotation(0, annotations.length, hiddenColumns);
+    }
+  }
+
+  /**
+   * delete any columns in alignmentAnnotation that are hidden (including
+   * sequence associated annotation).
+   * 
+   * @param start
+   *          remove any annotation to the right of this column
+   * @param end
+   *          remove any annotation to the left of this column
+   * @param hiddenColumns
+   *          the set of hidden columns
+   */
+  public void makeVisibleAnnotation(int start, int end,
+          HiddenColumns hiddenColumns)
+  {
+    if (annotations != null)
+    {
+      if (hiddenColumns.hasHiddenColumns())
+      {
+        removeHiddenAnnotation(start, end, hiddenColumns);
+      }
+      else
+      {
+        restrict(start, end);
+      }
+    }
+  }
+
+  /**
+   * The actual implementation of deleting hidden annotation columns
+   * 
+   * @param start
+   *          remove any annotation to the right of this column
+   * @param end
+   *          remove any annotation to the left of this column
+   * @param hiddenColumns
+   *          the set of hidden columns
+   */
+  private void removeHiddenAnnotation(int start, int end,
+          HiddenColumns hiddenColumns)
+  {
+    // mangle the alignmentAnnotation annotation array
+    ArrayList<Annotation[]> annels = new ArrayList<>();
+    Annotation[] els = null;
+
+    int w = 0;
+
+    Iterator<int[]> blocks = hiddenColumns.getVisContigsIterator(start,
+            end + 1, false);
+
+    int copylength;
+    int annotationLength;
+    while (blocks.hasNext())
+    {
+      int[] block = blocks.next();
+      annotationLength = block[1] - block[0] + 1;
+
+      if (blocks.hasNext())
+      {
+        // copy just the visible segment of the annotation row
+        copylength = annotationLength;
+      }
+      else
+      {
+        if (annotationLength + block[0] <= annotations.length)
+        {
+          // copy just the visible segment of the annotation row
+          copylength = annotationLength;
+        }
+        else
+        {
+          // copy to the end of the annotation row
+          copylength = annotations.length - block[0];
+        }
+      }
+
+      els = new Annotation[annotationLength];
+      annels.add(els);
+      System.arraycopy(annotations, block[0], els, 0, copylength);
+      w += annotationLength;
+    }
+
+    if (w != 0)
+    {
+      annotations = new Annotation[w];
+
+      w = 0;
+      for (Annotation[] chnk : annels)
+      {
+        System.arraycopy(chnk, 0, annotations, w, chnk.length);
+        w += chnk.length;
+      }
+    }
+  }
+
   public static Iterable<AlignmentAnnotation> findAnnotations(
           Iterable<AlignmentAnnotation> list, SequenceI seq, String calcId,
           String label)
@@ -1530,11 +1704,14 @@ public class AlignmentAnnotation
     }
     for (AlignmentAnnotation a : list)
     {
-      if (calcId.equals(a.getCalcId()))
+
+      if (a.getCalcId() == calcId || (a.getCalcId() != null
+              && calcId != null && a.getCalcId().equals(calcId)))
       {
         aa.add(a);
       }
     }
     return aa;
   }
+
 }