Merge branch 'Release_2_9_0b1_Branch'
[jalview.git] / src / jalview / analysis / Conservation.java
index eb68cc4..d868959 100755 (executable)
@@ -1,27 +1,35 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
- * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b1)
+ * Copyright (C) 2015 The Jalview Authors
  * 
- * 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
- * of the License, or (at your option) any later version.
+ * This file is part of Jalview.
  * 
- * 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 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.
+ *  
+ * 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.awt.Color;
-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
@@ -69,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;
@@ -102,14 +110,22 @@ public class Conservation
     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;
     }
   }
 
@@ -264,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));
             }
@@ -273,7 +288,10 @@ public class Conservation
         }
       }
 
-      total[i - start] = resultHash;
+      if (total.length > 0)
+      {
+        total[i - start] = resultHash;
+      }
     }
   }
 
@@ -332,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)
   {
@@ -355,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();
@@ -374,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++;
             }
           }
@@ -387,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++;
             }
           }
@@ -489,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)
   {
@@ -594,13 +623,21 @@ public class Conservation
   }
 
   /**
-   * complete the given consensus and quuality annotation rows
-   * @param conservation conservation annotation row
-   * @param quality2 (optional - may be null)
-   * @param alWidth extent of consensus
+   * 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 alWidth)
+          AlignmentAnnotation quality2, int istart, int alWidth)
   {
     char[] sequence = getConsSequence().getSequence();
     float minR;
@@ -615,7 +652,7 @@ public class Conservation
     maxR = 1.0f - minR;
     maxG = 0.9f - minG;
     maxB = 0f - minB; // scalable range for colouring both Conservation and
-                      // Quality
+    // Quality
 
     float min = 0f;
     float max = 11f;
@@ -624,12 +661,20 @@ public class Conservation
 
     char c;
 
-    conservation.annotations = new Annotation[alWidth];
+    if (conservation.annotations != null
+            && conservation.annotations.length < alWidth)
+    {
+      conservation.annotations = new Annotation[alWidth];
+    }
 
     if (quality2 != null)
     {
       quality2.graphMax = qualityRange[1].floatValue();
-      quality2.annotations = new Annotation[alWidth];
+      if (quality2.annotations != null
+              && quality2.annotations.length < alWidth)
+      {
+        quality2.annotations = new Annotation[alWidth];
+      }
       qmin = qualityRange[0].floatValue();
       qmax = qualityRange[1].floatValue();
     }
@@ -642,7 +687,7 @@ public class Conservation
 
       if (Character.isDigit(c))
       {
-        value = (int) (c - '0');
+        value = c - '0';
       }
       else if (c == '*')
       {
@@ -655,22 +700,81 @@ public class Conservation
 
       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),
-              String.valueOf(value), ' ', value, new Color(minR
-                      + (maxR * vprop), minG + (maxG * vprop), minB
-                      + (maxB * vprop)));
+              conssym, ' ', value, new Color(minR + (maxR * vprop), minG
+                      + (maxG * vprop), minB + (maxB * vprop)));
 
       // Quality calc
       if (quality2 != null)
       {
-        value = ((Double) quality.get(i)).floatValue();
+        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)));
+        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;
+  }
 }