JAL-1950 JAL-2937 allow copy of group without sequences being also copied to cloned...
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
index 676f318..cffe0d9 100755 (executable)
@@ -45,128 +45,115 @@ import java.util.Map;
 public class SequenceGroup implements AnnotatedCollectionI
 {
   // TODO ideally this event notification functionality should be separated into
-  // a
-  // subclass of ViewportProperties similarly to ViewportRanges. Done here as
-  // quick fix for JAL-2665
+  // a subclass of ViewportProperties similarly to ViewportRanges.
+  // Done here as a quick fix for JAL-2665
   public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
 
-  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
-          this);
+  private String groupName;
 
-  public void addPropertyChangeListener(PropertyChangeListener listener)
-  {
-    changeSupport.addPropertyChangeListener(listener);
-  }
+  private String description;
 
-  public void removePropertyChangeListener(PropertyChangeListener listener)
-  {
-    changeSupport.removePropertyChangeListener(listener);
-  }
-  // end of event notification functionality initialisation
+  private AnnotatedCollectionI context;
 
-  String groupName;
+  private Conservation conservationData;
 
-  String description;
+  private ProfilesI consensusProfiles;
 
-  Conservation conserve;
+  private ProfilesI hmmProfiles;
 
-  boolean displayBoxes = true;
+  private boolean displayBoxes = true;
 
-  boolean displayText = true;
+  private boolean displayText = true;
 
-  boolean colourText = false;
+  private boolean colourText = false;
 
-  /**
-   * True if the group is defined as a group on the alignment, false if it is
-   * just a selection.
+  /*
+   * true if the group is defined as a group on the alignment, false if it is
+   * just a selection
    */
-  boolean isDefined = false;
+  private boolean isDefined;
 
-  /**
+  /*
    * after Olivier's non-conserved only character display
    */
-  boolean showNonconserved = false;
+  private boolean showNonconserved;
 
-  /**
-   * group members
+  /*
+   * sequences in the group
    */
   private List<SequenceI> sequences = new ArrayList<>();
 
-  /**
+  /*
    * representative sequence for this group (if any)
    */
-  private SequenceI seqrep = null;
+  private SequenceI seqrep;
 
-  int width = -1;
+  private int width = -1;
 
-  /**
-   * Colourscheme applied to group if any
+  /*
+   * colour scheme applied to group if any
    */
   public ResidueShaderI cs;
 
   // start column (base 0)
-  int startRes = 0;
+  private int startRes;
 
   // end column (base 0)
-  int endRes = 0;
+  private int endRes;
 
   public Color outlineColour = Color.black;
 
-  public Color idColour = null;
+  public Color idColour;
 
-  public int thresholdTextColour = 0;
+  public int thresholdTextColour;
 
   public Color textColour = Color.black;
 
   public Color textColour2 = Color.white;
 
-  /**
-   * consensus calculation property
+  /*
+   * properties for consensus annotation
    */
   private boolean ignoreGapsInConsensus = true;
 
-  private boolean ignoreBelowBackground = true;
+  private boolean showSequenceLogo;
 
-  private boolean infoLetterHeight = false;
+  private boolean normaliseSequenceLogo;
 
-  /**
-   * consensus calculation property
-   */
-  private boolean showSequenceLogo = false;
+  private boolean showConsensusHistogram;
 
-  /**
-   * flag indicating if logo should be rendered normalised
+  /*
+   * properties for HMM information annotation
    */
-  private boolean normaliseSequenceLogo;
+  private boolean hmmIgnoreBelowBackground = true;
+
+  private boolean hmmUseInfoLetterHeight;
+
+  private boolean hmmShowSequenceLogo;
+
+  private boolean hmmNormaliseSequenceLogo;
+
+  private boolean hmmShowHistogram;
 
   /*
    * visibility of rows or represented rows covered by group
    */
-  private boolean hidereps = false;
+  private boolean hidereps;
 
   /*
    * visibility of columns intersecting this group
    */
-  private boolean hidecols = false;
-
-  AlignmentAnnotation consensus = null;
+  private boolean hidecols;
 
-  AlignmentAnnotation conservation = null;
+  private AlignmentAnnotation consensus;
 
-  AlignmentAnnotation information = null;
+  private AlignmentAnnotation conservation;
 
-  private boolean showConsensusHistogram;
-
-  private AnnotatedCollectionI context;
-
-  private boolean showHMMSequenceLogo;
-
-  private boolean normaliseHMMSequenceLogo;
-
-  private boolean showInformationHistogram;
+  private AlignmentAnnotation hmmInformation;
 
   /**
-   * Creates a new SequenceGroup object.
+   * Constructor, assigning a generated default name of "JGroup:" with object
+   * hashcode appended
    */
   public SequenceGroup()
   {
@@ -211,11 +198,27 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public SequenceGroup(SequenceGroup seqsel)
   {
+    this(seqsel, true);
+  }
+
+  /**
+   * copy constructor
+   * 
+   * @param seqsel
+   * @param keepsequences
+   *          if false do not add sequences from seqsel to new instance
+   */
+  public SequenceGroup(SequenceGroup seqsel, boolean keepsequences)
+  {
     this();
+
     if (seqsel != null)
     {
       sequences = new ArrayList<>();
-      sequences.addAll(seqsel.sequences);
+      if (keepsequences)
+      {
+        sequences.addAll(seqsel.sequences);
+      }
       if (seqsel.groupName != null)
       {
         groupName = new String(seqsel.groupName);
@@ -236,9 +239,9 @@ public class SequenceGroup implements AnnotatedCollectionI
       showSequenceLogo = seqsel.showSequenceLogo;
       normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
       showConsensusHistogram = seqsel.showConsensusHistogram;
-      showHMMSequenceLogo = seqsel.showHMMSequenceLogo;
-      normaliseHMMSequenceLogo = seqsel.normaliseHMMSequenceLogo;
-      showInformationHistogram = seqsel.showInformationHistogram;
+      hmmShowSequenceLogo = seqsel.hmmShowSequenceLogo;
+      hmmNormaliseSequenceLogo = seqsel.hmmNormaliseSequenceLogo;
+      hmmShowHistogram = seqsel.hmmShowHistogram;
       idColour = seqsel.idColour;
       outlineColour = seqsel.outlineColour;
       seqrep = seqsel.seqrep;
@@ -247,22 +250,38 @@ public class SequenceGroup implements AnnotatedCollectionI
       thresholdTextColour = seqsel.thresholdTextColour;
       width = seqsel.width;
       ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
-      ignoreBelowBackground = seqsel.ignoreBelowBackground;
-      infoLetterHeight = seqsel.infoLetterHeight;
-      if (seqsel.conserve != null)
+      hmmIgnoreBelowBackground = seqsel.hmmIgnoreBelowBackground;
+      hmmUseInfoLetterHeight = seqsel.hmmUseInfoLetterHeight;
+      if (keepsequences && seqsel.conservationData != null)
       {
+        // todo avoid doing this if we don't actually want derived calculations
+        // !
         recalcConservation(); // safer than
         // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
       }
     }
   }
 
+  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
+
+  public void addPropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  public void removePropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
+
   public boolean isShowSequenceLogo()
   {
     return showSequenceLogo;
   }
 
-  public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
+  public SequenceI[] getSelectionAsNewSequences(AlignmentI align,
+          boolean copyAnnotation)
   {
     int iSize = sequences.size();
     SequenceI[] seqs = new SequenceI[iSize];
@@ -276,15 +295,8 @@ public class SequenceGroup implements AnnotatedCollectionI
       if (seqs[ipos] != null)
       {
         seqs[ipos].setDescription(seq.getDescription());
-        seqs[ipos].setDBRefs(seq.getDBRefs());
-        seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
-        seqs[ipos].setIsHMMConsensusSequence(seq.isHMMConsensusSequence());
-        if (seq.getDatasetSequence() != null)
-        {
-          seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
-        }
 
-        if (seq.getAnnotation() != null)
+        if (seq.getAnnotation() != null && copyAnnotation)
         {
           AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
           // Only copy annotation that is either a score or referenced by the
@@ -508,7 +520,7 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public Conservation getConservation()
   {
-    return conserve;
+    return conservationData;
   }
 
   /**
@@ -519,7 +531,7 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public void setConservation(Conservation c)
   {
-    conserve = c;
+    conservationData = c;
   }
 
   /**
@@ -580,21 +592,22 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public boolean recalcConservation()
   {
-    return recalcConservation(false);
+    return recalcAnnotations(false);
   }
 
   /**
-   * calculate residue conservation for group - but only if necessary. returns
-   * true if the calculation resulted in a visible change to group
+   * Recalculates column consensus, conservation, and HMM annotation for the
+   * group (as applicable). Returns true if the calculation resulted in a
+   * visible change to group.
    * 
    * @param defer
    *          when set, colourschemes for this group are not refreshed after
    *          recalculation
    */
-  public boolean recalcConservation(boolean defer)
+  public boolean recalcAnnotations(boolean defer)
   {
     if (cs == null && consensus == null && conservation == null
-            && information == null)
+            && hmmInformation == null)
     {
       return false;
     }
@@ -605,15 +618,14 @@ public class SequenceGroup implements AnnotatedCollectionI
     {
       ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
               endRes + 1, showSequenceLogo);
-      if (information != null)
+      if (hmmInformation != null)
       {
-        HiddenMarkovModel hmm = information.sequenceRef.getHMM();
+        HiddenMarkovModel hmm = hmmInformation.sequenceRef.getHMM();
 
         ProfilesI info = AAFrequency.calculateHMMProfiles(hmm,
                 (endRes + 1) - startRes, startRes, endRes + 1,
-                showHMMSequenceLogo, ignoreBelowBackground,
-                infoLetterHeight);
-        _updateInformationRow(info, sequences.size());
+                hmmIgnoreBelowBackground, hmmUseInfoLetterHeight);
+        _updateInformationRow(info);
         upd = true;
       }
       if (consensus != null)
@@ -688,10 +700,6 @@ public class SequenceGroup implements AnnotatedCollectionI
     c.completeAnnotations(conservation, null, startRes, endRes + 1);
   }
 
-  public ProfilesI consensusData = null;
-
-  public ProfilesI informationData = null;
-
   private void _updateConsensusRow(ProfilesI cnsns, long nseq)
   {
     if (consensus == null)
@@ -700,7 +708,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
     consensus.label = "Consensus for " + getName();
     consensus.description = "Percent Identity";
-    consensusData = cnsns;
+    consensusProfiles = cnsns;
     // preserve width if already set
     int aWidth = (consensus.annotations != null)
             ? (endRes < consensus.annotations.length
@@ -718,31 +726,30 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * Recalculates the information content on the HMM annotation.
+   * Recalculates the information content on the HMM annotation
    * 
    * @param cnsns
-   * @param nseq
    */
-  private void _updateInformationRow(ProfilesI cnsns, long nseq)
+  private void _updateInformationRow(ProfilesI cnsns)
   {
-    if (information == null)
+    if (hmmInformation == null)
     {
-      getInformation();
+      createInformationAnnotation();
     }
-    information.description = MessageManager
+    hmmInformation.description = MessageManager
             .getString("label.information_description");
-    informationData = cnsns;
+    setHmmProfiles(cnsns);
     // preserve width if already set
-    int aWidth = (information.annotations != null)
-            ? (endRes < information.annotations.length
-                    ? information.annotations.length : endRes + 1)
+    int aWidth = (hmmInformation.annotations != null)
+            ? (endRes < hmmInformation.annotations.length
+                    ? hmmInformation.annotations.length : endRes + 1)
             : endRes + 1;
-    information.annotations = null;
-    information.annotations = new Annotation[aWidth]; // should be alignment
+    hmmInformation.annotations = null;
+    hmmInformation.annotations = new Annotation[aWidth]; // should be alignment
                                                       // width
-    information.setCalcId(InformationThread.HMM_CALC_ID);
-    AAFrequency.completeInformation(information, cnsns, startRes,
-            endRes + 1, nseq, 0f);
+    hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+    AAFrequency.completeInformation(hmmInformation, cnsns, startRes,
+            endRes + 1);
   }
 
   /**
@@ -1199,26 +1206,19 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * Answers the Hidden Markov Model annotation for this group (creating it if
-   * necessary)
-   * 
-   * @return
+   * Creates the Hidden Markov Model annotation for this group
    */
-  public AlignmentAnnotation getInformation()
+  void createInformationAnnotation()
   {
-    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 = MessageManager
-              .getString("label.information_description");
-      information.setCalcId(InformationThread.HMM_CALC_ID);
-    }
-    return information;
+    hmmInformation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
+            6.25f, AlignmentAnnotation.BAR_GRAPH);
+    hmmInformation.hasText = true;
+    hmmInformation.autoCalculated = false;
+    hmmInformation.groupRef = this;
+    hmmInformation.label = getName();
+    hmmInformation.description = MessageManager
+            .getString("label.information_description");
+    hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
   }
 
   /**
@@ -1274,9 +1274,10 @@ public class SequenceGroup implements AnnotatedCollectionI
     {
       if (consensus.annotations[i] != null)
       {
-        if (consensus.annotations[i].description.charAt(0) == '[')
+        String desc = consensus.annotations[i].description;
+        if (desc.length() > 1 && desc.charAt(0) == '[')
         {
-          seqs.append(consensus.annotations[i].description.charAt(1));
+          seqs.append(desc.charAt(1));
         }
         else
         {
@@ -1302,37 +1303,29 @@ public class SequenceGroup implements AnnotatedCollectionI
     ignoreGapsInConsensus = state;
   }
 
-  public boolean getIgnoreGapsConsensus()
+  public boolean isIgnoreGapsConsensus()
   {
     return ignoreGapsInConsensus;
   }
 
   public void setIgnoreBelowBackground(boolean state)
   {
-    if (this.ignoreBelowBackground != state)
-    {
-      ignoreBelowBackground = state;
-    }
-    ignoreBelowBackground = state;
+    hmmIgnoreBelowBackground = state;
   }
 
-  public boolean getIgnoreBelowBackground()
+  public boolean isIgnoreBelowBackground()
   {
-    return ignoreBelowBackground;
+    return hmmIgnoreBelowBackground;
   }
 
   public void setInfoLetterHeight(boolean state)
   {
-    if (this.infoLetterHeight != state)
-    {
-      infoLetterHeight = state;
-    }
-    infoLetterHeight = state;
+    hmmUseInfoLetterHeight = state;
   }
 
-  public boolean getInfoLetterHeight()
+  public boolean isUseInfoLetterHeight()
   {
-    return infoLetterHeight;
+    return hmmUseInfoLetterHeight;
   }
 
   /**
@@ -1581,59 +1574,67 @@ public class SequenceGroup implements AnnotatedCollectionI
 
   public boolean isShowInformationHistogram()
   {
-    return showInformationHistogram;
+    return hmmShowHistogram;
   }
 
   public void setShowInformationHistogram(boolean state)
   {
-    if (showInformationHistogram != state && information != null)
+    if (hmmShowHistogram != state && hmmInformation != null)
     {
-      this.showInformationHistogram = state;
+      this.hmmShowHistogram = state;
       // recalcConservation(); TODO don't know what to do here next
     }
-    this.showInformationHistogram = state;
-
+    this.hmmShowHistogram = state;
   }
 
   public boolean isShowHMMSequenceLogo()
   {
-    return showHMMSequenceLogo;
+    return hmmShowSequenceLogo;
   }
 
-  public void setshowHMMSequenceLogo(boolean state)
+  public void setShowHMMSequenceLogo(boolean state)
   {
-    showHMMSequenceLogo = state;
-
+    hmmShowSequenceLogo = state;
   }
 
   public boolean isNormaliseHMMSequenceLogo()
   {
-    return normaliseHMMSequenceLogo;
+    return hmmNormaliseSequenceLogo;
   }
 
   public void setNormaliseHMMSequenceLogo(boolean state)
   {
-    normaliseSequenceLogo = state;
+    hmmNormaliseSequenceLogo = state;
   }
 
-  /**
-   * Returns all HMM consensus sequences. This will not return real sequences
-   * with HMMs.
-   */
-  @Override
-  public List<SequenceI> getHMMConsensusSequences()
+  public ProfilesI getConsensusData()
+  {
+    return consensusProfiles;
+  }
+
+  public ProfilesI getHmmProfiles()
   {
-    List<SequenceI> seqs = new ArrayList<>();
+    return hmmProfiles;
+  }
 
-    for (int position = 0; position < sequences.size(); position++)
+  public void setHmmProfiles(ProfilesI hmmProfiles)
+  {
+    this.hmmProfiles = hmmProfiles;
+  }
+
+  @Override
+  public List<SequenceI> getHmmSequences()
+  {
+    List<SequenceI> result = new ArrayList<>();
+    for (int i = 0; i < sequences.size(); i++)
     {
-      SequenceI seq = sequences.get(position);
-      if (seq.isHMMConsensusSequence())
+      SequenceI seq = sequences.get(i);
+      if (seq.hasHMMProfile())
       {
-        seqs.add(seq);
+        result.add(seq);
       }
     }
-    return seqs;
+    return result;
   }
 
 }