make sure references are intact when group annotation is accessed
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
index 18fb53b..dcb0150 100755 (executable)
@@ -1,17 +1,17 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
- *
+ * 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
+ * 
  * 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 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.
- *
+ * 
  * 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
@@ -26,33 +26,78 @@ import jalview.analysis.*;
 import jalview.schemes.*;
 
 /**
- * DOCUMENT ME!
- *
+ * Collects a set contiguous ranges on a set of sequences
+ * 
  * @author $author$
  * @version $Revision$
  */
 public class SequenceGroup
 {
   String groupName;
+
   String description;
+
   Conservation conserve;
+
   Vector aaFrequency;
+
   boolean displayBoxes = true;
+
   boolean displayText = true;
+
   boolean colourText = false;
+  /**
+   * after Olivier's non-conserved only character display
+   */
+  boolean showUnconserved = false;
+  
+  /**
+   * group members
+   */
   private Vector sequences = new Vector();
+  /**
+   * representative sequence for this group (if any)
+   */
+  private SequenceI seqrep = null;
   int width = -1;
 
-  /** DOCUMENT ME!! */
+  /**
+   * Colourscheme applied to group if any */
   public ColourSchemeI cs;
+
   int startRes = 0;
+
   int endRes = 0;
-  Color outlineColour = Color.black;
+
+  public Color outlineColour = Color.black;
+
+  public Color idColour = null;
+
   public int thresholdTextColour = 0;
+
   public Color textColour = Color.black;
+
   public Color textColour2 = Color.white;
 
   /**
+   * consensus calculation property
+   */
+  private boolean ignoreGapsInConsensus=true;
+  /**
+   * consensus calculation property
+   */
+  private boolean showSequenceLogo=false;
+
+  /**
+   * @return the includeAllConsSymbols
+   */
+  public boolean isShowSequenceLogo()
+  {
+    return showSequenceLogo;
+  }
+
+
+  /**
    * Creates a new SequenceGroup object.
    */
   public SequenceGroup()
@@ -62,20 +107,21 @@ public class SequenceGroup
 
   /**
    * Creates a new SequenceGroup object.
-   *
-   * @param sequences DOCUMENT ME!
-   * @param groupName DOCUMENT ME!
-   * @param scheme DOCUMENT ME!
-   * @param displayBoxes DOCUMENT ME!
-   * @param displayText DOCUMENT ME!
-   * @param colourText DOCUMENT ME!
-   * @param start DOCUMENT ME!
-   * @param end DOCUMENT ME!
+   * 
+   * @param sequences
+   * @param groupName
+   * @param scheme
+   * @param displayBoxes
+   * @param displayText
+   * @param colourText
+   * @param start
+   *                first column of group
+   * @param end
+   *                last column of group
    */
   public SequenceGroup(Vector sequences, String groupName,
-                       ColourSchemeI scheme, boolean displayBoxes,
-                       boolean displayText,
-                       boolean colourText, int start, int end)
+          ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
+          boolean colourText, int start, int end)
   {
     this.sequences = sequences;
     this.groupName = groupName;
@@ -87,19 +133,61 @@ public class SequenceGroup
     endRes = end;
     recalcConservation();
   }
+  /**
+   * copy constructor
+   * @param seqsel
+   */
+  public SequenceGroup(SequenceGroup seqsel)
+  {
+    if (seqsel!=null)
+    {
+      sequences=new Vector();
+      Enumeration sq = seqsel.sequences.elements();
+      while (sq.hasMoreElements()) { 
+        sequences.addElement(sq.nextElement()); 
+      };
+      if (seqsel.groupName!=null)
+      {
+        groupName = new String(seqsel.groupName);
+      }
+      displayBoxes = seqsel.displayBoxes;
+      displayText = seqsel.displayText;
+      colourText = seqsel.colourText;
+      startRes = seqsel.startRes;
+      endRes = seqsel.endRes;
+      cs =seqsel.cs;
+      if (seqsel.description!=null)
+        description = new String(seqsel.description);
+      hidecols = seqsel.hidecols;
+      hidereps = seqsel.hidereps;
+      idColour = seqsel.idColour;
+      outlineColour = seqsel.outlineColour;
+      seqrep = seqsel.seqrep;
+      textColour = seqsel.textColour;
+      textColour2 = seqsel.textColour2;
+      thresholdTextColour = seqsel.thresholdTextColour;
+      width = seqsel.width;
+      ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
+      if (seqsel.conserve!=null)
+      {
+        recalcConservation(); // safer than 
+        // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
+      }
+    }
+  }
 
   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
   {
     int iSize = sequences.size();
     SequenceI[] seqs = new SequenceI[iSize];
     SequenceI[] inorder = getSequencesInOrder(align);
-    
-    for (int i = 0,ipos=0; i < inorder.length; i++)
+
+    for (int i = 0, ipos = 0; i < inorder.length; i++)
     {
       SequenceI seq = inorder[i];
 
-      seqs[ipos] = seq.getSubSequence(startRes, endRes+1);
-      if (seqs[ipos]!=null)
+      seqs[ipos] = seq.getSubSequence(startRes, endRes + 1);
+      if (seqs[ipos] != null)
       {
         seqs[ipos].setDescription(seq.getDescription());
         seqs[ipos].setDBRef(seq.getDBRef());
@@ -111,8 +199,26 @@ public class SequenceGroup
 
         if (seq.getAnnotation() != null)
         {
+          AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
+          // Only copy annotation that is either a score or referenced by the
+          // alignment's annotation vector
           for (int a = 0; a < seq.getAnnotation().length; a++)
           {
+            AlignmentAnnotation tocopy = seq.getAnnotation()[a];
+            if (alann != null)
+            {
+              boolean found = false;
+              for (int pos = 0; pos < alann.length; pos++)
+              {
+                if (alann[pos] == tocopy)
+                {
+                  found = true;
+                  break;
+                }
+              }
+              if (!found)
+                continue;
+            }
             AlignmentAnnotation newannot = new AlignmentAnnotation(seq
                     .getAnnotation()[a]);
             newannot.restrict(startRes, endRes);
@@ -122,11 +228,13 @@ public class SequenceGroup
           }
         }
         ipos++;
-      } else {
+      }
+      else
+      {
         iSize--;
       }
     }
-    if (iSize!=inorder.length)
+    if (iSize != inorder.length)
     {
       SequenceI[] nseqs = new SequenceI[iSize];
       System.arraycopy(seqs, 0, nseqs, 0, iSize);
@@ -137,9 +245,10 @@ public class SequenceGroup
   }
 
   /**
-   * If sequence ends in gaps, the end residue can
-   * be correctly calculated here
-   * @param seq SequenceI
+   * If sequence ends in gaps, the end residue can be correctly calculated here
+   * 
+   * @param seq
+   *                SequenceI
    * @return int
    */
   public int findEndRes(SequenceI seq)
@@ -150,7 +259,7 @@ public class SequenceGroup
     for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
     {
       ch = seq.getCharAt(j);
-      if (!jalview.util.Comparison.isGap( (ch)))
+      if (!jalview.util.Comparison.isGap((ch)))
       {
         eres++;
       }
@@ -184,8 +293,7 @@ public class SequenceGroup
           for (int h = 0; h < hsg.getSize(); h++)
           {
             seq2 = hsg.getSequenceAt(h);
-            if (seq2 != seq
-                && !allSequences.contains(seq2))
+            if (seq2 != seq && !allSequences.contains(seq2))
             {
               allSequences.addElement(seq2);
             }
@@ -215,9 +323,10 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param col DOCUMENT ME!
-   *
+   * 
+   * @param col
+   *                DOCUMENT ME!
+   * 
    * @return DOCUMENT ME!
    */
   public boolean adjustForRemoveLeft(int col)
@@ -248,9 +357,10 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param col DOCUMENT ME!
-   *
+   * 
+   * @param col
+   *                DOCUMENT ME!
+   * 
    * @return DOCUMENT ME!
    */
   public boolean adjustForRemoveRight(int col)
@@ -271,7 +381,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public String getName()
@@ -286,12 +396,14 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param name DOCUMENT ME!
+   * 
+   * @param name
+   *                DOCUMENT ME!
    */
   public void setName(String name)
   {
     groupName = name;
+    // TODO: URGENT: update dependent objects (annotation row)
   }
 
   public void setDescription(String desc)
@@ -301,7 +413,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public Conservation getConservation()
@@ -311,8 +423,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param c DOCUMENT ME!
+   * 
+   * @param c
+   *                DOCUMENT ME!
    */
   public void setConservation(Conservation c)
   {
@@ -320,10 +433,12 @@ public class SequenceGroup
   }
 
   /**
-   * DOCUMENT ME!
-   *
-   * @param s DOCUMENT ME!
-   * @param recalc DOCUMENT ME!
+   * Add s to this sequence group
+   * 
+   * @param s
+   *                alignment sequence to be added
+   * @param recalc
+   *                true means Group's conservation should be recalculated
    */
   public void addSequence(SequenceI s, boolean recalc)
   {
@@ -339,54 +454,103 @@ public class SequenceGroup
   }
 
   /**
-   * DOCUMENT ME!
+   * Max Gaps Threshold for performing a conservation calculation
+   * TODO: make this a configurable property - or global to an alignment view 
+   */
+  private int consPercGaps=25;
+  /**
+   * calculate residue conservation for group - but only if necessary.
    */
   public void recalcConservation()
   {
-    if (cs == null)
+    if (cs == null && consensus == null && conservation == null)
     {
       return;
     }
-
+    
     try
     {
-      cs.setConsensus(AAFrequency.calculate(sequences, startRes, endRes + 1));
-
+      Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes, endRes + 1, showSequenceLogo);
+      if (consensus != null)
+      {
+        _updateConsensusRow(cnsns);
+      }
+      if (cs!=null)
+      {
+        cs.setConsensus(cnsns);
+      
       if (cs instanceof ClustalxColourScheme)
       {
-        ( (ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
+        ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
+      }
       }
 
-      if (cs.conservationApplied())
+      if ((conservation!=null) || (cs!=null && cs.conservationApplied()))
       {
         Conservation c = new Conservation(groupName,
-                                          ResidueProperties.propHash, 3,
-                                          sequences,
-                                          startRes, endRes + 1);
+                ResidueProperties.propHash, 3, sequences, startRes,
+                endRes + 1);
         c.calculate();
-        c.verdict(false, 25);
-
-        cs.setConservation(c);
-
+        c.verdict(false, consPercGaps);
+        if (conservation!=null)
+        {
+          _updateConservationRow(c);
+        }
+        if (cs!=null)
+        {
+          cs.setConservation(c);
+        
         if (cs instanceof ClustalxColourScheme)
         {
-          ( (ClustalxColourScheme) cs).resetClustalX(sequences,
-              getWidth());
+          ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
+        }
         }
       }
-    }
-    catch (java.lang.OutOfMemoryError err)
+    } catch (java.lang.OutOfMemoryError err)
     {
+      // TODO: catch OOM
       System.out.println("Out of memory loading groups: " + err);
     }
 
   }
 
+  private void _updateConservationRow(Conservation c)
+  {
+    if (conservation==null)
+    {
+      getConservation();
+    }
+    // update Labels
+    conservation.label = "Conservation for "+getName();
+    conservation.description = "Conservation for group "+getName()+" less than " + consPercGaps
+            + "% gaps";
+    // preserve width if already set
+    int aWidth = (conservation.annotations!=null) ? (endRes<conservation.annotations.length ? conservation.annotations.length : endRes+1) : endRes+1;
+    conservation.annotations = null;
+    conservation.annotations = new Annotation[aWidth]; // should be alignment width
+    c.completeAnnotations(conservation,null, startRes, endRes+1);
+  }
+  public Hashtable[] consensusData = null;
+  private void _updateConsensusRow(Hashtable[] cnsns)
+  {
+    if (consensus==null)
+    {
+      getConsensus();
+    }
+    consensus.label = "Consensus for "+getName();
+    consensus.description = "Percent Identity";
+    consensusData = cnsns;
+    // preserve width if already set
+    int aWidth = (consensus.annotations!=null) ? (endRes<consensus.annotations.length ? consensus.annotations.length : endRes+1) : endRes+1;
+    consensus.annotations = null;
+    consensus.annotations = new Annotation[aWidth]; // should be alignment width
+
+    AAFrequency.completeConsensus(consensus,cnsns,startRes,endRes+1,ignoreGapsInConsensus, showSequenceLogo); // TODO: setting container for ignoreGapsInConsensusCalculation);
+  }
+
   /**
-   * DOCUMENT ME!
-   *
-   * @param s DOCUMENT ME!
-   * @param recalc DOCUMENT ME!
+   * @param s sequence to either add or remove from group
+   * @param recalc flag passed to delete/addSequence to indicate if group properties should be recalculated
    */
   public void addOrRemove(SequenceI s, boolean recalc)
   {
@@ -402,9 +566,11 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param s DOCUMENT ME!
-   * @param recalc DOCUMENT ME!
+   * 
+   * @param s
+   *                DOCUMENT ME!
+   * @param recalc
+   *                DOCUMENT ME!
    */
   public void deleteSequence(SequenceI s, boolean recalc)
   {
@@ -418,7 +584,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public int getStartRes()
@@ -428,7 +594,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public int getEndRes()
@@ -438,8 +604,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param i DOCUMENT ME!
+   * 
+   * @param i
+   *                DOCUMENT ME!
    */
   public void setStartRes(int i)
   {
@@ -448,8 +615,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param i DOCUMENT ME!
+   * 
+   * @param i
+   *                DOCUMENT ME!
    */
   public void setEndRes(int i)
   {
@@ -458,7 +626,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public int getSize()
@@ -468,9 +636,10 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param i DOCUMENT ME!
-   *
+   * 
+   * @param i
+   *                DOCUMENT ME!
+   * 
    * @return DOCUMENT ME!
    */
   public SequenceI getSequenceAt(int i)
@@ -480,8 +649,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param state DOCUMENT ME!
+   * 
+   * @param state
+   *                DOCUMENT ME!
    */
   public void setColourText(boolean state)
   {
@@ -490,7 +660,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public boolean getColourText()
@@ -500,8 +670,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param state DOCUMENT ME!
+   * 
+   * @param state
+   *                DOCUMENT ME!
    */
   public void setDisplayText(boolean state)
   {
@@ -510,7 +681,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public boolean getDisplayText()
@@ -520,8 +691,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param state DOCUMENT ME!
+   * 
+   * @param state
+   *                DOCUMENT ME!
    */
   public void setDisplayBoxes(boolean state)
   {
@@ -530,7 +702,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public boolean getDisplayBoxes()
@@ -540,7 +712,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public int getWidth()
@@ -548,7 +720,7 @@ public class SequenceGroup
     // MC This needs to get reset when characters are inserted and deleted
     if (sequences.size() > 0)
     {
-      width = ( (SequenceI) sequences.elementAt(0)).getLength();
+      width = ((SequenceI) sequences.elementAt(0)).getLength();
     }
 
     for (int i = 1; i < sequences.size(); i++)
@@ -566,8 +738,9 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
-   * @param c DOCUMENT ME!
+   * 
+   * @param c
+   *                DOCUMENT ME!
    */
   public void setOutlineColour(Color c)
   {
@@ -576,7 +749,7 @@ public class SequenceGroup
 
   /**
    * DOCUMENT ME!
-   *
+   * 
    * @return DOCUMENT ME!
    */
   public Color getOutlineColour()
@@ -585,11 +758,13 @@ public class SequenceGroup
   }
 
   /**
-   *
-   * returns the sequences in the group ordered by the ordering given by al
-   *
-   * @param al Alignment
-   * @return SequenceI[]
+   * 
+   * returns the sequences in the group ordered by the ordering given by al.
+   * this used to return an array with null entries regardless, new behaviour is below.
+   * TODO: verify that this does not affect use in applet or application
+   * @param al
+   *                Alignment
+   * @return SequenceI[] intersection of sequences in group with al, ordered by al, or null if group does not intersect with al
    */
   public SequenceI[] getSequencesInOrder(AlignmentI al)
   {
@@ -606,7 +781,283 @@ public class SequenceGroup
         seqs[index++] = al.getSequenceAt(i);
       }
     }
-
+    if (index==0)
+    {
+      return null;
+    }
+    if (index<seqs.length)
+    {
+      SequenceI[] dummy = seqs;
+      seqs = new SequenceI[index];
+      while (--index>=0)
+      {
+        seqs[index] = dummy[index];
+        dummy[index] = null;
+      }
+    }
     return seqs;
   }
-}
+
+  /**
+   * @return the idColour
+   */
+  public Color getIdColour()
+  {
+    return idColour;
+  }
+
+  /**
+   * @param idColour
+   *                the idColour to set
+   */
+  public void setIdColour(Color idColour)
+  {
+    this.idColour = idColour;
+  }
+
+  /**
+   * @return the representative sequence for this group
+   */
+  public SequenceI getSeqrep()
+  {
+    return seqrep;
+  }
+
+  /**
+   * set the representative sequence for this group.
+   * Note - this affects the interpretation of the Hidereps attribute.
+   * @param seqrep the seqrep to set (null means no sequence representative)
+   */
+  public void setSeqrep(SequenceI seqrep)
+  {
+    this.seqrep = seqrep;
+  }
+  /**
+   * 
+   * @return true if group has a sequence representative
+   */
+  public boolean hasSeqrep()
+  {
+    return seqrep != null;
+  }
+  /**
+   * visibility of rows or represented rows covered by group
+   */
+  private boolean hidereps=false;
+  /**
+   * set visibility of sequences covered by (if no sequence representative is defined) 
+   * or represented by this group.
+   * @param visibility
+   */
+  public void setHidereps(boolean visibility)
+  {
+    hidereps = visibility;
+  }
+  /**
+   * 
+   * @return true if sequences represented (or covered) by this group should be hidden
+   */
+  public boolean isHidereps()
+  {
+    return hidereps;
+  }
+  /**
+   * visibility of columns intersecting this group
+   */
+  private boolean hidecols=false;
+  /**
+   * set intended visibility of columns covered by this group
+   * @param visibility
+   */
+  public void setHideCols(boolean visibility)
+  {
+    hidecols = visibility;
+  }
+  /**
+   * 
+   * @return true if columns covered by group should be hidden
+   */
+  public boolean isHideCols()
+  {
+    return hidecols;
+  }
+  /**
+   * create a new sequence group from the intersection of this group
+   * with an alignment Hashtable of hidden representatives
+   * 
+   * @param alignment (may not be null)
+   * @param hashtable (may be null)
+   * @return new group containing sequences common to this group and alignment
+   */
+  public SequenceGroup intersect(AlignmentI alignment, Hashtable hashtable)
+  {
+    SequenceGroup sgroup = new SequenceGroup(this);
+    SequenceI[] insect=getSequencesInOrder(alignment);
+    sgroup.sequences = new Vector();
+    for (int s=0;insect!=null && s<insect.length;s++) { 
+      if (hashtable==null || hashtable.containsKey(insect[s]))
+              {
+      sgroup.sequences.addElement(insect[s]); }
+    }
+    //Enumeration en =getSequences(hashtable).elements();
+    //while (en.hasMoreElements())
+   // {
+   //   SequenceI elem = (SequenceI) en.nextElement();
+   //   if (alignment.getSequences().contains(elem))
+   //   {
+   //     sgroup.addSequence(elem, false);
+   //   }
+   // }
+    return sgroup;
+  }
+
+  /**
+   * @return the showUnconserved
+   */
+  public boolean getShowunconserved()
+  {
+    return showUnconserved;
+  }
+
+  /**
+   * @param showUnconserved the showUnconserved to set
+   */
+  public void setShowunconserved(boolean displayNonconserved)
+  {
+    this.showUnconserved = displayNonconserved;
+  }
+  AlignmentAnnotation consensus=null,conservation=null;
+
+  /**
+   * flag indicating if consensus histogram should be rendered
+   */
+  private boolean showConsensusHistogram;
+
+  /**
+   * 
+   * @return automatically calculated consensus row
+   */
+  public AlignmentAnnotation getConsensus()
+  {
+    // TODO get or calculate and get consensus annotation row for this group
+    int aWidth = this.getWidth();
+    // pointer
+    // possibility
+    // here.
+    if (aWidth < 0)
+    {
+      return null;
+    }
+    if (consensus==null)
+    {
+       consensus = new AlignmentAnnotation("","",
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+    }
+    consensus.hasText = true;
+    consensus.autoCalculated = true;
+    consensus.groupRef = this;
+    consensus.label = "Consensus for "+getName();
+    consensus.description = "Percent Identity";
+    return consensus;
+  }
+    /**
+   * get the conservation annotation row for this group
+   * @return autoCalculated annotation row
+   */
+  public AlignmentAnnotation getConservationRow() {
+    if (conservation == null) {
+      conservation = new AlignmentAnnotation("","", new Annotation[1], 0f, 11f,
+            AlignmentAnnotation.BAR_GRAPH);
+    }
+    
+    conservation.hasText = true;
+    conservation.autoCalculated = true;
+    conservation.groupRef = this;
+    conservation.label = "Conservation for "+getName();
+    conservation.description = "Conservation for group "+getName()+" less than " + consPercGaps
+            + "% gaps";
+    return conservation;
+  }
+
+  /**
+   * 
+   * @return true if annotation rows have been instantiated for this group
+   */
+    public boolean hasAnnotationRows()
+    {
+      return consensus!=null || conservation!=null;
+    }
+
+  public SequenceI getConsensusSeq()
+  {
+    getConsensus();
+    StringBuffer seqs = new StringBuffer();
+    for (int i = 0; i < consensus.annotations.length; i++)
+    {
+      if (consensus.annotations[i] != null)
+      {
+        if (consensus.annotations[i].description.charAt(0) == '[')
+        {
+          seqs.append(consensus.annotations[i].description.charAt(1));
+        }
+        else
+        {
+          seqs.append(consensus.annotations[i].displayCharacter);
+        }
+      }
+    }
+
+    SequenceI sq = new Sequence("Group"+getName()+" Consensus", seqs.toString());
+    sq.setDescription("Percentage Identity Consensus "
+            + ((ignoreGapsInConsensus) ? " without gaps" : ""));
+    return sq;    
+  }
+
+  public void setIgnoreGapsConsensus(boolean state)
+  {
+    if (this.ignoreGapsInConsensus!=state && consensus!=null)
+    {
+      ignoreGapsInConsensus = state;
+      recalcConservation();
+    }
+    ignoreGapsInConsensus = state;
+  }
+  public boolean getIgnoreGapsConsensus()
+  {
+    return ignoreGapsInConsensus;
+  }
+  /**
+   * @param includeAllConsSymbols the includeAllConsSymbols to set
+   */
+  public void setIncludeAllConsSymbols(boolean includeAllConsSymbols)
+  {
+    if (this.showSequenceLogo!=includeAllConsSymbols && consensus!=null) {
+      this.showSequenceLogo = includeAllConsSymbols;
+      recalcConservation();
+    }
+    this.showSequenceLogo = includeAllConsSymbols;
+  }
+
+
+  /**
+   * 
+   * @param showConsHist flag indicating if the consensus histogram for this group should be rendered
+   */
+  public void setShowConsensusHistogram(boolean showConsHist)
+  {
+    
+    if (showConsensusHistogram!=showConsHist && consensus!=null) {
+      this.showConsensusHistogram = showConsHist;
+      recalcConservation();
+    }
+    this.showConsensusHistogram = showConsHist;
+  }
+  /**
+   * @return the showConsensusHistogram
+   */
+  public boolean isShowConsensusHistogram()
+  {
+    return showConsensusHistogram;
+  }
+  }