JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / analysis / Conservation.java
index c216471..f12d801 100755 (executable)
@@ -1,26 +1,35 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
+ * Copyright (C) 2015 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
  * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.analysis;
 
-import java.util.*;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
 
-import jalview.datamodel.*;
+import java.awt.Color;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
 
 /**
  * Calculates conservation values for a given set of sequences
@@ -45,9 +54,10 @@ public class Conservation
   Hashtable[] total;
 
   boolean canonicaliseAa = true; // if true then conservation calculation will
-                                  // map all symbols to canonical aa numbering
-                                  // rather than consider conservation of that
-                                  // symbol
+
+  // map all symbols to canonical aa numbering
+  // rather than consider conservation of that
+  // symbol
 
   /** Stores calculated quality values */
   public Vector quality;
@@ -67,27 +77,27 @@ public class Conservation
 
   int[][] cons2;
 
+  private String[] consSymbs;
+
   /**
    * Creates a new Conservation object.
    * 
    * @param name
-   *                Name of conservation
+   *          Name of conservation
    * @param propHash
-   *                hash of properties for each symbol
+   *          hash of properties for each symbol
    * @param threshold
-   *                to count the residues in residueHash(). commonly used value
-   *                is 3
+   *          to count the residues in residueHash(). commonly used value is 3
    * @param sequences
-   *                sequences to be used in calculation
+   *          sequences to be used in calculation
    * @param start
-   *                start residue position
+   *          start residue position
    * @param end
-   *                end residue position
+   *          end residue position
    */
   public Conservation(String name, Hashtable propHash, int threshold,
-          Vector sequences, int start, int end)
+          List<SequenceI> sequences, int start, int end)
   {
-
     this.name = name;
     this.propHash = propHash;
     this.threshold = threshold;
@@ -95,25 +105,33 @@ public class Conservation
     this.end = end;
 
     maxLength = end - start + 1; // default width includes bounds of
-                                  // calculation
+    // calculation
 
     int s, sSize = sequences.size();
     SequenceI[] sarray = new SequenceI[sSize];
     this.sequences = sarray;
-
-    for (s = 0; s < sSize; s++)
+    try
     {
-      sarray[s] = (SequenceI) sequences.elementAt(s);
-      if (sarray[s].getLength() > maxLength)
+      for (s = 0; s < sSize; s++)
       {
-        maxLength = sarray[s].getLength();
+        sarray[s] = sequences.get(s);
+        if (sarray[s].getLength() > maxLength)
+        {
+          maxLength = sarray[s].getLength();
+        }
       }
+    } catch (ArrayIndexOutOfBoundsException ex)
+    {
+      // bail - another thread has modified the sequence array, so the current
+      // calculation is probably invalid.
+      this.sequences = new SequenceI[0];
+      maxLength = 0;
     }
   }
 
   /**
-   * Translate sequence i into a numerical 
-   * representation and store it in the i'th position of the seqNums array.
+   * Translate sequence i into a numerical representation and store it in the
+   * i'th position of the seqNums array.
    * 
    * @param i
    */
@@ -146,7 +164,7 @@ public class Conservation
         }
 
         sqnum = new int[len + 1]; // better to always make a new array -
-                                  // sequence can change its length
+        // sequence can change its length
         sqnum[0] = sq.hashCode();
 
         for (j = 1; j <= len; j++)
@@ -262,8 +280,7 @@ public class Conservation
                 resultHash.put(type, ht.get("-"));
               }
             }
-            else if (((Integer) resultHash.get(type)).equals((Integer) ht
-                    .get(res)) == false)
+            else if (((Integer) resultHash.get(type)).equals(ht.get(res)) == false)
             {
               resultHash.put(type, new Integer(-1));
             }
@@ -271,12 +288,16 @@ public class Conservation
         }
       }
 
-      total[i - start] = resultHash;
+      if (total.length > 0)
+      {
+        total[i - start] = resultHash;
+      }
     }
   }
 
   /*****************************************************************************
-   * count conservation  for the j'th column of the alignment
+   * count conservation for the j'th column of the alignment
+   * 
    * @return { gap count, conserved residue count}
    */
   public int[] countConsNGaps(int j)
@@ -329,10 +350,10 @@ public class Conservation
    * Calculates the conservation sequence
    * 
    * @param consflag
-   *                if true, poitiveve conservation; false calculates negative
-   *                conservation
+   *          if true, poitiveve conservation; false calculates negative
+   *          conservation
    * @param percentageGaps
-   *                commonly used value is 25
+   *          commonly used value is 25
    */
   public void verdict(boolean consflag, float percentageGaps)
   {
@@ -352,17 +373,17 @@ public class Conservation
     {
       consString.append('-');
     }
-
+    consSymbs = new String[end - start + 1];
     for (int i = start; i <= end; i++)
     {
       gapcons = countConsNGaps(i);
       totGaps = gapcons[1];
-      pgaps = ((float) totGaps * 100) / (float) sequences.length;
+      pgaps = ((float) totGaps * 100) / sequences.length;
+      consSymbs[i - start] = new String();
 
       if (percentageGaps > pgaps)
       {
         resultHash = total[i - start];
-
         // Now find the verdict
         count = 0;
         enumeration = resultHash.keys();
@@ -371,12 +392,12 @@ public class Conservation
         {
           type = (String) enumeration.nextElement();
           result = (Integer) resultHash.get(type);
-
           // Do we want to count +ve conservation or +ve and -ve cons.?
           if (consflag)
           {
             if (result.intValue() == 1)
             {
+              consSymbs[i - start] = type + " " + consSymbs[i - start];
               count++;
             }
           }
@@ -384,6 +405,17 @@ public class Conservation
           {
             if (result.intValue() != -1)
             {
+              {
+                if (result.intValue() == 0)
+                {
+                  consSymbs[i - start] = consSymbs[i - start] + " !" + type;
+                }
+                else
+                {
+                  consSymbs[i - start] = type + " " + consSymbs[i - start];
+                }
+              }
+
               count++;
             }
           }
@@ -477,8 +509,7 @@ public class Conservation
        * maxj = -1;
        * 
        * for (int j=0;j<24;j++) { if (cons2[i][j] > max) { max = cons2[i][j];
-       * maxi = i; maxj = j; }
-       *  } }
+       * maxi = i; maxj = j; } } }
        */
     }
   }
@@ -487,9 +518,9 @@ public class Conservation
    * Calculates the quality of the set of sequences
    * 
    * @param start
-   *                Start residue
+   *          Start residue
    * @param end
-   *                End residue
+   *          End residue
    */
   public void findQuality(int start, int end)
   {
@@ -590,4 +621,160 @@ public class Conservation
     qualityRange[0] = new Double(0);
     qualityRange[1] = new Double(newmax);
   }
+
+  /**
+   * complete the given consensus and quuality annotation rows. Note: currently
+   * this method will enlarge the given annotation row if it is too small,
+   * otherwise will leave its length unchanged.
+   * 
+   * @param conservation
+   *          conservation annotation row
+   * @param quality2
+   *          (optional - may be null)
+   * @param istart
+   *          first column for conservation
+   * @param alWidth
+   *          extent of conservation
+   */
+  public void completeAnnotations(AlignmentAnnotation conservation,
+          AlignmentAnnotation quality2, int istart, int alWidth)
+  {
+    char[] sequence = getConsSequence().getSequence();
+    float minR;
+    float minG;
+    float minB;
+    float maxR;
+    float maxG;
+    float maxB;
+    minR = 0.3f;
+    minG = 0.0f;
+    minB = 0f;
+    maxR = 1.0f - minR;
+    maxG = 0.9f - minG;
+    maxB = 0f - minB; // scalable range for colouring both Conservation and
+    // Quality
+
+    float min = 0f;
+    float max = 11f;
+    float qmin = 0f;
+    float qmax = 0f;
+
+    char c;
+
+    if (conservation.annotations != null
+            && conservation.annotations.length < alWidth)
+    {
+      conservation.annotations = new Annotation[alWidth];
+    }
+
+    if (quality2 != null)
+    {
+      quality2.graphMax = qualityRange[1].floatValue();
+      if (quality2.annotations != null
+              && quality2.annotations.length < alWidth)
+      {
+        quality2.annotations = new Annotation[alWidth];
+      }
+      qmin = qualityRange[0].floatValue();
+      qmax = qualityRange[1].floatValue();
+    }
+
+    for (int i = 0; i < alWidth; i++)
+    {
+      float value = 0;
+
+      c = sequence[i];
+
+      if (Character.isDigit(c))
+      {
+        value = c - '0';
+      }
+      else if (c == '*')
+      {
+        value = 11;
+      }
+      else if (c == '+')
+      {
+        value = 10;
+      }
+
+      float vprop = value - min;
+      vprop /= max;
+      int consp = i - start;
+      String conssym = (value > 0 && consp > -1 && consp < consSymbs.length) ? consSymbs[consp]
+              : "";
+      conservation.annotations[i] = new Annotation(String.valueOf(c),
+              conssym, ' ', value, new Color(minR + (maxR * vprop), minG
+                      + (maxG * vprop), minB + (maxB * vprop)));
+
+      // Quality calc
+      if (quality2 != null)
+      {
+        value = ((Double) quality.elementAt(i)).floatValue();
+        vprop = value - qmin;
+        vprop /= qmax;
+        quality2.annotations[i] = new Annotation(" ",
+                String.valueOf(value), ' ', value, new Color(minR
+                        + (maxR * vprop), minG + (maxG * vprop), minB
+                        + (maxB * vprop)));
+      }
+    }
+  }
+
+  /**
+   * construct and call the calculation methods on a new Conservation object
+   * 
+   * @param name
+   *          - name of conservation
+   * @param consHash
+   *          - hash table of properties for each amino acid (normally
+   *          ResidueProperties.propHash)
+   * @param threshold
+   *          - minimum number of conserved residues needed to indicate
+   *          conservation (typically 3)
+   * @param seqs
+   * @param start
+   *          first column in calculation window
+   * @param end
+   *          last column in calculation window
+   * @param posOrNeg
+   *          positive (true) or negative (false) conservation
+   * @param consPercGaps
+   *          percentage of gaps tolerated in column
+   * @param calcQuality
+   *          flag indicating if alignment quality should be calculated
+   * @return Conservation object ready for use in visualization
+   */
+  public static Conservation calculateConservation(String name,
+          Hashtable consHash, int threshold, List<SequenceI> seqs,
+          int start, int end, boolean posOrNeg, int consPercGaps,
+          boolean calcQuality)
+  {
+    Conservation cons = new Conservation(name, consHash, threshold, seqs,
+            start, end);
+    return calculateConservation(cons, posOrNeg, consPercGaps, calcQuality);
+  }
+
+  /**
+   * @param b
+   *          positive (true) or negative (false) conservation
+   * @param consPercGaps
+   *          percentage of gaps tolerated in column
+   * @param calcQuality
+   *          flag indicating if alignment quality should be calculated
+   * @return Conservation object ready for use in visualization
+   */
+  public static Conservation calculateConservation(Conservation cons,
+          boolean b, int consPercGaps, boolean calcQuality)
+  {
+    cons.calculate();
+    cons.verdict(b, consPercGaps);
+
+    if (calcQuality)
+    {
+      cons.findQuality();
+    }
+
+    return cons;
+  }
 }