JAL-2629 HMMs dropped on the alignment now map to the rf annotation
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
index e37c55e..c1443ef 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.analysis.Conservation;
 import jalview.renderer.ResidueShader;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
+import jalview.util.MessageManager;
 
 import java.awt.Color;
 import java.util.ArrayList;
@@ -52,6 +53,12 @@ public class SequenceGroup implements AnnotatedCollectionI
   boolean colourText = false;
 
   /**
+   * True if the group is defined as a group on the alignment, false if it is
+   * just a selection.
+   */
+  boolean isDefined = false;
+
+  /**
    * after Olivier's non-conserved only character display
    */
   boolean showNonconserved = false;
@@ -59,7 +66,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   /**
    * group members
    */
-  private List<SequenceI> sequences = new ArrayList<SequenceI>();
+  private List<SequenceI> sequences = new ArrayList<>();
 
   /**
    * representative sequence for this group (if any)
@@ -94,6 +101,10 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   private boolean ignoreGapsInConsensus = true;
 
+  private boolean ignoreBelowBackground = true;
+
+  private boolean infoLetterHeight = false;
+
   /**
    * consensus calculation property
    */
@@ -104,13 +115,31 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   private boolean normaliseSequenceLogo;
 
-  /**
-   * @return the includeAllConsSymbols
+  /*
+   * visibility of rows or represented rows covered by group
    */
-  public boolean isShowSequenceLogo()
-  {
-    return showSequenceLogo;
-  }
+  private boolean hidereps = false;
+
+  /*
+   * visibility of columns intersecting this group
+   */
+  private boolean hidecols = false;
+
+  AlignmentAnnotation consensus = null;
+
+  AlignmentAnnotation conservation = null;
+
+  AlignmentAnnotation information = null;
+
+  private boolean showConsensusHistogram;
+
+  private AnnotatedCollectionI context;
+
+  private boolean showHMMSequenceLogo;
+
+  private boolean normaliseHMMSequenceLogo;
+
+  private boolean showInformationHistogram;
 
   /**
    * Creates a new SequenceGroup object.
@@ -161,7 +190,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     this();
     if (seqsel != null)
     {
-      sequences = new ArrayList<SequenceI>();
+      sequences = new ArrayList<>();
       sequences.addAll(seqsel.sequences);
       if (seqsel.groupName != null)
       {
@@ -172,13 +201,20 @@ public class SequenceGroup implements AnnotatedCollectionI
       colourText = seqsel.colourText;
       startRes = seqsel.startRes;
       endRes = seqsel.endRes;
-      cs = seqsel.cs;
+      cs = new ResidueShader((ResidueShader) seqsel.cs);
       if (seqsel.description != null)
       {
         description = new String(seqsel.description);
       }
       hidecols = seqsel.hidecols;
       hidereps = seqsel.hidereps;
+      showNonconserved = seqsel.showNonconserved;
+      showSequenceLogo = seqsel.showSequenceLogo;
+      normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
+      showConsensusHistogram = seqsel.showConsensusHistogram;
+      showHMMSequenceLogo = seqsel.showHMMSequenceLogo;
+      normaliseHMMSequenceLogo = seqsel.normaliseHMMSequenceLogo;
+      showInformationHistogram = seqsel.showInformationHistogram;
       idColour = seqsel.idColour;
       outlineColour = seqsel.outlineColour;
       seqrep = seqsel.seqrep;
@@ -187,6 +223,8 @@ public class SequenceGroup implements AnnotatedCollectionI
       thresholdTextColour = seqsel.thresholdTextColour;
       width = seqsel.width;
       ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
+      ignoreBelowBackground = seqsel.ignoreBelowBackground;
+      infoLetterHeight = seqsel.infoLetterHeight;
       if (seqsel.conserve != null)
       {
         recalcConservation(); // safer than
@@ -195,6 +233,11 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
   }
 
+  public boolean isShowSequenceLogo()
+  {
+    return showSequenceLogo;
+  }
+
   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
   {
     int iSize = sequences.size();
@@ -311,7 +354,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
     else
     {
-      List<SequenceI> allSequences = new ArrayList<SequenceI>();
+      List<SequenceI> allSequences = new ArrayList<>();
       for (SequenceI seq : sequences)
       {
         allSequences.add(seq);
@@ -523,7 +566,8 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public boolean recalcConservation(boolean defer)
   {
-    if (cs == null && consensus == null && conservation == null)
+    if (cs == null && consensus == null && conservation == null
+            && information == null)
     {
       return false;
     }
@@ -534,6 +578,17 @@ public class SequenceGroup implements AnnotatedCollectionI
     {
       ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
               endRes + 1, showSequenceLogo);
+      if (information != null)
+      {
+        HiddenMarkovModel hmm = information.sequenceRef.getHMM();
+
+        ProfilesI info = AAFrequency.calculateHMMProfiles(hmm,
+                (endRes + 1) - startRes, startRes, endRes + 1,
+                showHMMSequenceLogo, ignoreBelowBackground,
+                infoLetterHeight);
+        _updateInformationRow(info, sequences.size());
+        upd = true;
+      }
       if (consensus != null)
       {
         _updateConsensusRow(cnsns, sequences.size());
@@ -595,8 +650,10 @@ public class SequenceGroup implements AnnotatedCollectionI
     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)
+    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
@@ -606,6 +663,8 @@ public class SequenceGroup implements AnnotatedCollectionI
 
   public ProfilesI consensusData = null;
 
+  public ProfilesI informationData = null;
+
   private void _updateConsensusRow(ProfilesI cnsns, long nseq)
   {
     if (consensus == null)
@@ -616,8 +675,10 @@ public class SequenceGroup implements AnnotatedCollectionI
     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)
+    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
@@ -630,6 +691,38 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
+   * Recalculates the information content on the HMM annotation.
+   * 
+   * @param cnsns
+   * @param nseq
+   */
+  private void _updateInformationRow(ProfilesI cnsns, long nseq)
+  {
+    if (information == null)
+    {
+      getInformation();
+    }
+    information.description = MessageManager
+            .getString("label.information_description");
+    informationData = cnsns;
+    // preserve width if already set
+    int aWidth = (information.annotations != null)
+            ? (endRes < information.annotations.length
+                    ? information.annotations.length : endRes + 1)
+            : endRes + 1;
+    information.annotations = null;
+    information.annotations = new Annotation[aWidth]; // should be alignment
+                                                      // width
+    information.calcId = "HMM";
+    AAFrequency.completeInformation(information, cnsns, startRes,
+            endRes + 1, nseq, 0f); // TODO:
+                                                                        // setting
+                                                            // container
+    // for
+    // ignoreGapsInInformationCalculation);
+  }
+
+  /**
    * @param s
    *          sequence to either add or remove from group
    * @param recalc
@@ -951,11 +1044,6 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * 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.
    * 
@@ -977,11 +1065,6 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * visibility of columns intersecting this group
-   */
-  private boolean hidecols = false;
-
-  /**
    * set intended visibility of columns covered by this group
    * 
    * @param visibility
@@ -1015,7 +1098,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     SequenceGroup sgroup = new SequenceGroup(this);
     SequenceI[] insect = getSequencesInOrder(alignment);
-    sgroup.sequences = new ArrayList<SequenceI>();
+    sgroup.sequences = new ArrayList<>();
     for (int s = 0; insect != null && s < insect.length; s++)
     {
       if (map == null || map.containsKey(insect[s]))
@@ -1043,13 +1126,6 @@ public class SequenceGroup implements AnnotatedCollectionI
     this.showNonconserved = displayNonconserved;
   }
 
-  AlignmentAnnotation consensus = null, conservation = null;
-
-  /**
-   * flag indicating if consensus histogram should be rendered
-   */
-  private boolean showConsensusHistogram;
-
   /**
    * set this alignmentAnnotation object as the one used to render consensus
    * annotation
@@ -1094,6 +1170,35 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
+   * 
+   * @return information content annotation.
+   */
+  public AlignmentAnnotation getInformation()
+  {
+    // TODO get or calculate and get information annotation row for this group
+    int aWidth = this.getWidth();
+    // pointer
+    // possibility
+    // here.
+    if (aWidth < 0)
+    {
+      return null;
+    }
+    if (information == null)
+    {
+      information = new AlignmentAnnotation("", "", new Annotation[1], 0f,
+              6.25f, AlignmentAnnotation.BAR_GRAPH);
+      information.hasText = true;
+      information.autoCalculated = false;
+      information.groupRef = this;
+      information.label = getName();
+      information.description = "Information content, measured in bits";
+      information.calcId = "HMM";
+    }
+    return information;
+  }
+
+  /**
    * set this alignmentAnnotation object as the one used to render consensus
    * annotation
    * 
@@ -1179,6 +1284,34 @@ public class SequenceGroup implements AnnotatedCollectionI
     return ignoreGapsInConsensus;
   }
 
+  public void setIgnoreBelowBackground(boolean state)
+  {
+    if (this.ignoreBelowBackground != state)
+    {
+      ignoreBelowBackground = state;
+    }
+    ignoreBelowBackground = state;
+  }
+
+  public boolean getIgnoreBelowBackground()
+  {
+    return ignoreBelowBackground;
+  }
+
+  public void setInfoLetterHeight(boolean state)
+  {
+    if (this.infoLetterHeight != state)
+    {
+      infoLetterHeight = state;
+    }
+    infoLetterHeight = state;
+  }
+
+  public boolean getInfoLetterHeight()
+  {
+    return infoLetterHeight;
+  }
+
   /**
    * @param showSequenceLogo
    *          indicates if a sequence logo is shown for consensus annotation
@@ -1242,7 +1375,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     // TODO add in other methods like 'getAlignmentAnnotation(String label),
     // etc'
-    ArrayList<AlignmentAnnotation> annot = new ArrayList<AlignmentAnnotation>();
+    ArrayList<AlignmentAnnotation> annot = new ArrayList<>();
     synchronized (sequences)
     {
       for (SequenceI seq : sequences)
@@ -1274,7 +1407,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   @Override
   public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
   {
-    List<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
+    List<AlignmentAnnotation> aa = new ArrayList<>();
     if (calcId == null)
     {
       return aa;
@@ -1289,22 +1422,19 @@ public class SequenceGroup implements AnnotatedCollectionI
     return aa;
   }
 
-  /**
-   * Returns a list of annotations that match the specified sequenceRef, calcId
-   * and label, ignoring null values.
-   * 
-   * @return list of AlignmentAnnotation objects
-   */
   @Override
   public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
           String calcId, String label)
   {
-    ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
+    ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
     for (AlignmentAnnotation ann : getAlignmentAnnotation())
     {
-      if (ann.getCalcId() != null && ann.getCalcId().equals(calcId)
-              && ann.sequenceRef != null && ann.sequenceRef == seq
-              && ann.label != null && ann.label.equals(label))
+      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);
       }
@@ -1344,12 +1474,29 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
   }
 
-  private AnnotatedCollectionI context;
+  /**
+   * Sets the alignment or group context for this group, and whether it is
+   * defined as a group
+   * 
+   * @param ctx
+   *          the context for the group
+   * @param defined
+   *          whether the group is defined on the alignment or is just a
+   *          selection
+   * @throws IllegalArgumentException
+   *           if setting the context would result in a circular reference chain
+   */
+  public void setContext(AnnotatedCollectionI ctx, boolean defined)
+  {
+    setContext(ctx);
+    this.isDefined = defined;
+  }
 
   /**
    * Sets the alignment or group context for this group
    * 
    * @param ctx
+   *          the context for the group
    * @throws IllegalArgumentException
    *           if setting the context would result in a circular reference chain
    */
@@ -1358,7 +1505,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     AnnotatedCollectionI ref = ctx;
     while (ref != null)
     {
-      if (ref == this)
+      if (ref == this || ref.getContext() == ctx)
       {
         throw new IllegalArgumentException(
                 "Circular reference in SequenceGroup.context");
@@ -1379,6 +1526,11 @@ public class SequenceGroup implements AnnotatedCollectionI
     return context;
   }
 
+  public boolean isDefined()
+  {
+    return isDefined;
+  }
+
   public void setColourScheme(ColourSchemeI scheme)
   {
     if (cs == null)
@@ -1406,7 +1558,8 @@ public class SequenceGroup implements AnnotatedCollectionI
   @Override
   public boolean isNucleotide()
   {
-    if (context != null) {
+    if (context != null)
+    {
       return context.isNucleotide();
     }
     return false;
@@ -1431,4 +1584,87 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
   }
+
+  public boolean isShowInformationHistogram()
+  {
+    return showInformationHistogram;
+  }
+
+  public void setShowInformationHistogram(boolean state)
+  {
+    if (showInformationHistogram != state && information != null)
+    {
+      this.showInformationHistogram = state;
+      // recalcConservation(); TODO don't know what to do here next
+    }
+    this.showInformationHistogram = state;
+
+  }
+
+  public boolean isShowHMMSequenceLogo()
+  {
+    // TODO Auto-generated method stub
+    return showHMMSequenceLogo;
+  }
+
+  public void setshowHMMSequenceLogo(boolean state)
+  {
+    showHMMSequenceLogo = state;
+
+  }
+
+  public boolean isNormaliseHMMSequenceLogo()
+  {
+    // TODO Auto-generated method stub
+    return normaliseHMMSequenceLogo;
+  }
+
+  public void setNormaliseHMMSequenceLogo(boolean state)
+  {
+    normaliseSequenceLogo = state;
+  }
+
+  /**
+   * Returns all HMM consensus sequences. This will not return real sequences
+   * with HMMs. If remove is set to true, the consensus sequences will be
+   * removed from the alignment.
+   */
+  @Override // TODO make this more efficient.
+  public List<SequenceI> getHMMConsensusSequences(boolean remove)
+  {
+    List<SequenceI> seqs = new ArrayList<>();
+    int position = 0;
+    int seqsRemoved = 0;
+    boolean endReached = false;
+
+    while (!endReached)
+    {
+      SequenceI seq = sequences.get(position);
+      if (seq.isHMMConsensusSequence())
+      {
+        if (remove)
+        {
+          sequences.remove(position);
+          seqsRemoved++;
+          seq.setPreviousPosition(seqsRemoved + position - 1);
+        }
+        else
+        {
+          position++;
+        }
+        seqs.add(seq);
+      }
+      else
+      {
+        position++;
+      }
+
+      if (position >= sequences.size())
+      {
+        endReached = true;
+      }
+    }
+    return seqs;
+  }
+
 }