JAL-1601 Expand secondary structure prediction tests
[jalview.git] / src / jalview / datamodel / AlignmentAnnotation.java
index 6c33c1b..40dfdd9 100755 (executable)
  */
 package jalview.datamodel;
 
-import jalview.analysis.Rna;
-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;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import jalview.analysis.Rna;
+import jalview.analysis.SecStrConsensus.SimpleBP;
+import jalview.analysis.WUSSParseException;
+
 /**
  * DOCUMENT ME!
  * 
@@ -93,6 +95,10 @@ public class AlignmentAnnotation
    */
   private long invalidrnastruc = -2;
 
+  private double bitScore;
+
+  private double eValue;
+
   /**
    * Updates the _rnasecstr field Determines the positions that base pair and
    * the positions of helices based on secondary structure from a Stockholm file
@@ -164,6 +170,72 @@ 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 +366,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,27 +374,35 @@ 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 ?
+        // allow for DSSP extended code:
+        // https://www.wikidoc.org/index.php/Secondary_structure#The_DSSP_code
+        // GHITEBS as well as C and X (for missing?)
         if (annotations[i].secondaryStructure == '('
                 || annotations[i].secondaryStructure == '['
                 || annotations[i].secondaryStructure == '<'
                 || annotations[i].secondaryStructure == '{'
                 || annotations[i].secondaryStructure == 'A'
-                || annotations[i].secondaryStructure == 'B'
-                || annotations[i].secondaryStructure == 'C'
+                // || 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 == 'I'
+                // || annotations[i].secondaryStructure == 'G'
+                // || annotations[i].secondaryStructure == 'H' // ambiguous on
+                // its own -- already checked above
+                // || annotations[i].secondaryStructure == 'I'
                 || annotations[i].secondaryStructure == 'J'
                 || annotations[i].secondaryStructure == 'K'
                 || annotations[i].secondaryStructure == 'L'
@@ -331,12 +412,12 @@ public class AlignmentAnnotation
                 || annotations[i].secondaryStructure == 'P'
                 || annotations[i].secondaryStructure == 'Q'
                 || annotations[i].secondaryStructure == 'R'
-                || annotations[i].secondaryStructure == 'S'
-                || annotations[i].secondaryStructure == 'T'
+                // || annotations[i].secondaryStructure == 'S'
+                // || annotations[i].secondaryStructure == 'T'
                 || annotations[i].secondaryStructure == 'U'
                 || annotations[i].secondaryStructure == 'V'
                 || annotations[i].secondaryStructure == 'W'
-                || annotations[i].secondaryStructure == 'X'
+                // || annotations[i].secondaryStructure == 'X'
                 || annotations[i].secondaryStructure == 'Y'
                 || annotations[i].secondaryStructure == 'Z')
         {
@@ -367,7 +448,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'
@@ -472,12 +553,12 @@ public class AlignmentAnnotation
                       : annotations[index + offset].displayCharacter == null
                               || annotations[index
                                       + offset].displayCharacter
-                                              .length() == 0
-                                                      ? annotations[index
-                                                              + offset].secondaryStructure
-                                                      : annotations[index
-                                                              + offset].displayCharacter
-                                                                      .charAt(0));
+                                      .length() == 0
+                                              ? annotations[index
+                                                      + offset].secondaryStructure
+                                              : annotations[index
+                                                      + offset].displayCharacter
+                                                      .charAt(0));
     }
 
     @Override
@@ -519,7 +600,7 @@ public class AlignmentAnnotation
     }
     return null;
   }
-
+  
   /**
    * Creates a new AlignmentAnnotation object.
    * 
@@ -631,7 +712,7 @@ public class AlignmentAnnotation
       }
     }
   }
-
+  
   /**
    * Copy constructor creates a new independent annotation row with the same
    * associated sequenceRef
@@ -641,6 +722,17 @@ public class AlignmentAnnotation
   public AlignmentAnnotation(AlignmentAnnotation annotation)
   {
     setAnnotationId();
+    updateAlignmentAnnotationFrom(annotation);
+  }
+
+  /**
+   * copy attributes and annotation from an existing annotation (used by copy
+   * constructor). This method does not update the unique annotationId
+   * 
+   * @param annotation
+   */
+  public void updateAlignmentAnnotationFrom(AlignmentAnnotation annotation)
+  {
     this.label = new String(annotation.label);
     if (annotation.description != null)
     {
@@ -664,6 +756,9 @@ public class AlignmentAnnotation
     this.scaleColLabel = annotation.scaleColLabel;
     this.showAllColLabels = annotation.showAllColLabels;
     this.calcId = annotation.calcId;
+    this.bitScore = annotation.bitScore;
+    this.eValue = annotation.eValue;
+
     if (annotation.properties != null)
     {
       properties = new HashMap<>();
@@ -759,15 +854,15 @@ public class AlignmentAnnotation
     }
     if (startRes >= annotations.length)
     {
-      startRes = annotations.length - 1;
+      startRes = annotations.length;
     }
-    if (endRes >= annotations.length)
+    if (endRes < 0)
     {
-      endRes = annotations.length - 1;
+      endRes = -1;
     }
-    if (annotations == null)
+    if (endRes >= annotations.length)
     {
-      return;
+      endRes = annotations.length - 1;
     }
     Annotation[] temp = new Annotation[endRes - startRes + 1];
     if (startRes < annotations.length)
@@ -895,6 +990,7 @@ public class AlignmentAnnotation
    * @param seqRef
    * @param startRes
    * @param alreadyMapped
+   *          - annotation are at aligned columns
    */
   public void createSequenceMapping(SequenceI seqRef, int startRes,
           boolean alreadyMapped)
@@ -926,7 +1022,7 @@ public class AlignmentAnnotation
           seqPos = i + startRes;
         }
 
-        sequenceMapping.put(new Integer(seqPos), annotations[i]);
+        sequenceMapping.put(Integer.valueOf(seqPos), annotations[i]);
       }
     }
 
@@ -965,7 +1061,7 @@ public class AlignmentAnnotation
     {
       for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
       {
-        index = new Integer(a);
+        index = Integer.valueOf(a);
         Annotation annot = sequenceMapping.get(index);
         if (annot != null)
         {
@@ -1096,7 +1192,7 @@ public class AlignmentAnnotation
   {
     return hasScore || !Double.isNaN(score);
   }
-
+  
   /**
    * Score only annotation
    * 
@@ -1127,6 +1223,7 @@ public class AlignmentAnnotation
     makeVisibleAnnotation(hidden);
   }
 
+
   public void setPadGaps(boolean padgaps, char gapchar)
   {
     this.padGaps = padgaps;
@@ -1160,7 +1257,7 @@ public class AlignmentAnnotation
   {
     if (seqname && this.sequenceRef != null)
     {
-      int i = description.toLowerCase().indexOf("<html>");
+      int i = description.toLowerCase(Locale.ROOT).indexOf("<html>");
       if (i > -1)
       {
         // move the html tag to before the sequence reference.
@@ -1484,7 +1581,7 @@ public class AlignmentAnnotation
   {
     if (annotations != null)
     {
-      makeVisibleAnnotation(0, annotations.length, hiddenColumns);
+      makeVisibleAnnotation(0, annotations.length - 1, hiddenColumns);
     }
   }
 
@@ -1528,6 +1625,8 @@ public class AlignmentAnnotation
   private void removeHiddenAnnotation(int start, int end,
           HiddenColumns hiddenColumns)
   {
+    start = Math.min(Math.max(0, start), annotations.length);
+    end = Math.min(Math.max(-1, end), annotations.length - 1);
     // mangle the alignmentAnnotation annotation array
     ArrayList<Annotation[]> annels = new ArrayList<>();
     Annotation[] els = null;
@@ -1582,4 +1681,117 @@ public class AlignmentAnnotation
     }
   }
 
+  public static Iterable<AlignmentAnnotation> findAnnotations(
+          Iterable<AlignmentAnnotation> list, SequenceI seq, String calcId,
+          String label)
+  {
+    ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
+    for (AlignmentAnnotation ann : list)
+    {
+      if ((calcId == null || (ann.getCalcId() != null
+              && ann.getCalcId().equals(calcId)))
+              && (seq == null || (ann.sequenceRef != null
+                      && ann.sequenceRef == seq))
+              && (label == null
+                      || (ann.label != null && ann.label.equals(label))))
+      {
+        aa.add(ann);
+      }
+    }
+    return aa;
+  }
+
+  /**
+   * Answer true if any annotation matches the calcId passed in (if not null).
+   * 
+   * @param list
+   *          annotation to search
+   * @param calcId
+   * @return
+   */
+  public static boolean hasAnnotation(List<AlignmentAnnotation> list,
+          String calcId)
+  {
+
+    if (calcId != null && !"".equals(calcId))
+    {
+      for (AlignmentAnnotation a : list)
+      {
+        if (a.getCalcId() == calcId)
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  public static Iterable<AlignmentAnnotation> findAnnotation(
+          List<AlignmentAnnotation> list, String calcId)
+  {
+    List<AlignmentAnnotation> aa = new ArrayList<>();
+    if (calcId == null)
+    {
+      return aa;
+    }
+    for (AlignmentAnnotation a : list)
+    {
+
+      if (a.getCalcId() == calcId || (a.getCalcId() != null
+              && calcId != null && a.getCalcId().equals(calcId)))
+      {
+        aa.add(a);
+      }
+    }
+    return aa;
+  }
+
+  public String getLabel()
+  {
+    return label;
+  }
+
+  public Annotation[] getAnnotations()
+  {
+    return annotations;
+  }
+
+  public double getBitScore()
+  {
+    return bitScore;
+  }
+
+  public void setBitScore(double bitScore)
+  {
+    this.bitScore = bitScore;
+  }
+
+  public double getEValue()
+  {
+    return eValue;
+  }
+
+  public void setEValue(double eValue)
+  {
+    this.eValue = eValue;
+  }
+
+  public static AlignmentAnnotation findFirstAnnotation(
+          Iterable<AlignmentAnnotation> alignmentAnnotation, String name,
+          String calcId, boolean autoCalc, SequenceI seqRef,
+          SequenceGroup groupRef)
+  {
+
+    for (AlignmentAnnotation annot : alignmentAnnotation)
+    {
+      if (annot.autoCalculated == autoCalc && (name.equals(annot.label))
+              && (calcId == null || annot.getCalcId().equals(calcId))
+              && annot.sequenceRef == seqRef && annot.groupRef == groupRef)
+      {
+        return annot;
+      }
+    }
+    return null;
+  }
+
 }