score and visible copy construction
[jalview.git] / src / jalview / datamodel / AlignmentAnnotation.java
index c675a75..acb02b1 100755 (executable)
@@ -18,6 +18,9 @@
  */
 package jalview.datamodel;
 
+import java.util.Enumeration;
+import java.util.Hashtable;
+
 /**
  * DOCUMENT ME!
  *
@@ -51,6 +54,15 @@ public class AlignmentAnnotation
   /** DOCUMENT ME!! */
   public float graphMax;
 
+  /**
+   * Score associated with label and description.
+   */
+  public float score= Float.NaN;
+  /**
+   * flag indicating if annotation has a score.
+   */
+  public boolean hasScore=false;
+
   public GraphLine threshold;
 
   // Graphical hints and tips
@@ -76,12 +88,17 @@ public class AlignmentAnnotation
 
   public int graphHeight = 40;
 
+  public boolean padGaps = true;
+
   public static final int NO_GRAPH = 0;
 
   public static final int BAR_GRAPH = 1;
 
   public static final int LINE_GRAPH = 2;
 
+  public boolean belowAlignment = true;
+
+
   public static int getGraphValueFromString(String string)
   {
     if (string.equalsIgnoreCase("BAR_GRAPH"))
@@ -103,7 +120,8 @@ public class AlignmentAnnotation
    *
    * @param label DOCUMENT ME!
    * @param description DOCUMENT ME!
-   * @param annotations DOCUMENT ME!
+   * @param annotations DOCUMENT ME!about:blank
+Loading...
    */
   public AlignmentAnnotation(String label, String description,
                              Annotation[] annotations)
@@ -114,7 +132,7 @@ public class AlignmentAnnotation
     this.description = description;
     this.annotations = annotations;
 
-    areLabelsSecondaryStructure();
+     validateRangeAndDisplay();
   }
 
   void areLabelsSecondaryStructure()
@@ -124,33 +142,39 @@ public class AlignmentAnnotation
     {
       if (annotations[i] == null)
       {
+        padGaps = false;
         continue;
       }
-
       if (annotations[i].secondaryStructure == 'H' ||
           annotations[i].secondaryStructure == 'E')
       {
-        hasIcons = true;
+          hasIcons = true;
       }
 
+      if(annotations[i].displayCharacter==null)
+        continue;
+
+
       if (annotations[i].displayCharacter.length() == 1
           && !annotations[i].displayCharacter.equals("H")
           && !annotations[i].displayCharacter.equals("E")
           && !annotations[i].displayCharacter.equals("-")
           && !annotations[i].displayCharacter.equals("."))
-      {
-        if (jalview.schemes.ResidueProperties.aaIndex
-            [annotations[i].displayCharacter.charAt(0)] < 23)
         {
-          nonSSLabel = true;
+          if (jalview.schemes.ResidueProperties.aaIndex
+                  [annotations[i].displayCharacter.charAt(0)] < 23)
+          {
+            nonSSLabel = true;
+          }
         }
-      }
 
-      if (annotations[i].displayCharacter.length() > 0)
-      {
-        hasText = true;
+        if (annotations[i].displayCharacter.length() > 0)
+        {
+          hasText = true;
+        }
+        else
+          padGaps = false;
       }
-    }
 
     if (nonSSLabel)
     {
@@ -165,12 +189,10 @@ public class AlignmentAnnotation
         }
 
       }
-
     }
 
     annotationId = this.hashCode() + "";
   }
-
   /**
    * Creates a new AlignmentAnnotation object.
    *
@@ -186,11 +208,34 @@ public class AlignmentAnnotation
                              int graphType)
   {
     // graphs are not editable
+    editable = graphType==0;
+
     this.label = label;
     this.description = description;
     this.annotations = annotations;
     graph = graphType;
+    graphMin = min;
+    graphMax = max;
+    validateRangeAndDisplay();
+  }
+  /**
+   * checks graphMin and graphMax,
+   * secondary structure symbols,
+   * sets graphType appropriately,
+   * sets null labels to the empty string
+   * if appropriate.
+   */
+  private void validateRangeAndDisplay() {
+
+    if (annotations==null)
+    {
+      visible=false; // try to prevent renderer from displaying.
+      return; // this is a non-annotation row annotation - ie a sequence score.
+    }
 
+    int graphType = graph;
+    float min = graphMin;
+    float max = graphMax;
     boolean drawValues = true;
 
     if (min == max)
@@ -203,7 +248,9 @@ public class AlignmentAnnotation
           continue;
         }
 
-        if (drawValues && annotations[i].displayCharacter.length() > 1)
+        if (drawValues
+            && annotations[i].displayCharacter!=null
+            && annotations[i].displayCharacter.length() > 1)
         {
           drawValues = false;
         }
@@ -238,6 +285,132 @@ public class AlignmentAnnotation
   }
 
   /**
+   * Copy constructor
+   * creates a new independent annotation row with the same associated sequenceRef
+   * @param annotation
+   */
+  public AlignmentAnnotation(AlignmentAnnotation annotation)
+  {
+    this.label = new String(annotation.label);
+    if (annotation.description != null)
+      this.description = new String(annotation.description);
+    this.graphMin = annotation.graphMin;
+    this.graphMax = annotation.graphMax;
+    this.graph = annotation.graph;
+    this.graphHeight = annotation.graphHeight;
+    this.graphGroup = annotation.graphGroup;
+    this.editable = annotation.editable;
+    this.autoCalculated = annotation.autoCalculated;
+    this.hasIcons = annotation.hasIcons;
+    this.hasText = annotation.hasText;
+    this.height = annotation.height;
+    this.label = annotation.label;
+    this.padGaps = annotation.padGaps;
+    this.visible = annotation.visible;
+    if (this.hasScore = annotation.hasScore)
+    {
+      this.score = annotation.score;
+    }
+    if (threshold!=null) {
+      threshold = new GraphLine(annotation.threshold);
+    }
+    if (annotation.annotations!=null) {
+      Annotation[] ann = annotation.annotations;
+      this.annotations = new Annotation[ann.length];
+      for (int i=0; i<ann.length; i++) {
+        annotations[i] = new Annotation(ann[i]);
+      };
+      if (annotation.sequenceRef!=null) {
+        this.sequenceRef = annotation.sequenceRef;
+        if (annotation.sequenceMapping!=null)
+        {
+          Integer p=null;
+          sequenceMapping = new Hashtable();
+          Enumeration pos=annotation.sequenceMapping.keys();
+          while (pos.hasMoreElements()) {
+            // could optimise this!
+            p = (Integer) pos.nextElement();
+            Annotation a = (Annotation) annotation.sequenceMapping.get(p);
+            if (a==null)
+            {
+              continue;
+            }
+            for (int i=0; i<ann.length; i++)
+            {
+              if (ann[i]==a)
+              {
+                sequenceMapping.put(p, annotations[i]);
+              }
+            }
+          }
+        } else {
+          this.sequenceMapping = null;
+        }
+      }
+    }
+    validateRangeAndDisplay(); // construct hashcodes, etc.
+  }
+
+  /**
+   * clip the annotation to the columns given by startRes and endRes (inclusive)
+   * and prune any existing sequenceMapping to just those columns.
+   * @param startRes
+   * @param endRes
+   */
+  public void restrict(int startRes, int endRes)
+  {
+    if (annotations==null)
+      return;
+    Annotation[] temp = new Annotation[endRes-startRes+1];
+    if (startRes<annotations.length)
+    {
+      System.arraycopy(annotations, startRes, temp, 0, Math.min(endRes, annotations.length-1)-startRes+1);
+    }
+    if (sequenceRef!=null) {
+      // Clip the mapping, if it exists.
+      int spos = sequenceRef.findPosition(startRes);
+      int epos = sequenceRef.findPosition(endRes);
+      if (sequenceMapping!=null)
+      {
+        Hashtable newmapping = new Hashtable();
+        Enumeration e = sequenceMapping.keys();
+        while (e.hasMoreElements())
+        {
+          Integer pos = (Integer) e.nextElement();
+          if (pos.intValue()>=spos && pos.intValue()<=epos)
+          {
+            newmapping.put(pos, sequenceMapping.get(pos));
+          }
+        }
+        sequenceMapping.clear();
+        sequenceMapping = newmapping;
+      }
+    }
+    annotations=temp;
+  }
+  /**
+   * set the annotation row to be at least length Annotations
+   * @param length minimum number of columns required in the annotation row
+   * @return false if the annotation row is greater than length
+   */
+  public boolean padAnnotation(int length) {
+    if (annotations==null)
+    {
+      annotations = new Annotation[length];
+      return true;
+    }
+    if (annotations.length<length)
+    {
+      Annotation[] na = new Annotation[length];
+      System.arraycopy(annotations, 0, na, 0, annotations.length);
+      annotations = na;
+      return true;
+    }
+    return annotations.length>length;
+
+  }
+
+  /**
    * DOCUMENT ME!
    *
    * @return DOCUMENT ME!
@@ -310,7 +483,10 @@ public class AlignmentAnnotation
     {
       return;
     }
-
+    if (annotations==null)
+    {
+      return;
+    }
     sequenceMapping = new java.util.Hashtable();
 
     sequenceRef = seqRef;
@@ -337,6 +513,14 @@ public class AlignmentAnnotation
 
   public void adjustForAlignment()
   {
+    if (sequenceRef==null)
+      return;
+
+    if (annotations==null)
+    {
+      return;
+    }
+
     int a = 0, aSize = sequenceRef.getLength();
 
     if (aSize == 0)
@@ -362,4 +546,94 @@ public class AlignmentAnnotation
 
     annotations = temp;
   }
+  /**
+   * remove any null entries in annotation row and return the
+   * number of non-null annotation elements.
+   * @return
+   */
+  private int compactAnnotationArray() {
+    int j=0;
+    for (int i=0;i<annotations.length; i++) {
+      if (annotations[i]!=null && j!=i) {
+        annotations[j++] = annotations[i];
+      }
+    }
+    Annotation[] ann = annotations;
+    annotations = new Annotation[j];
+    System.arraycopy(ann, 0, annotations, 0, j);
+    ann = null;
+    return j;
+  }
+
+  /**
+   * Associate this annotion with the aligned residues of a particular sequence.
+   * sequenceMapping will be updated in the following way:
+   *   null sequenceI - existing mapping will be discarded but annotations left in mapped positions.
+   *   valid sequenceI not equal to current sequenceRef: mapping is discarded and rebuilt assuming 1:1 correspondence
+   *   TODO: overload with parameter to specify correspondence between current and new sequenceRef
+   * @param sequenceI
+   */
+  public void setSequenceRef(SequenceI sequenceI)
+  {
+    if (sequenceI != null)
+    {
+      if (sequenceRef != null)
+      {
+        if (sequenceRef != sequenceI && !sequenceRef.equals(sequenceI) && sequenceRef.getDatasetSequence()!=sequenceI.getDatasetSequence())
+        {
+          // if sequenceRef isn't intersecting with sequenceI
+          // throw away old mapping and reconstruct.
+          sequenceRef = null;
+          if (sequenceMapping != null)
+          {
+            sequenceMapping = null;
+            // compactAnnotationArray();
+          }
+          createSequenceMapping(sequenceI, 1, true);
+          adjustForAlignment();
+        }
+        else
+        {
+          // Mapping carried over
+          sequenceRef = sequenceI;
+        }
+      }
+      else
+      {
+        // No mapping exists
+        createSequenceMapping(sequenceI, 1, true);
+        adjustForAlignment();
+      }
+    }
+    else
+    {
+      // throw away the mapping without compacting.
+      sequenceMapping = null;
+      sequenceRef = null;
+    }
+  }
+
+  /**
+   * @return the score
+   */
+  public float getScore()
+  {
+    return score;
+  }
+
+  /**
+   * @param score the score to set
+   */
+  public void setScore(float score)
+  {
+    this.score = score;
+  }
+  /**
+   *
+   * @return true if annotation has an associated score
+   */
+  public boolean hasScore()
+  {
+    return hasScore;
+  }
 }