Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
index 46c802f..7e53c46 100755 (executable)
  */
 package jalview.datamodel;
 
+import java.awt.Color;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.AAFrequency;
 import jalview.analysis.Conservation;
 import jalview.renderer.ResidueShader;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
 
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Collects a set contiguous ranges on a set of sequences
  * 
@@ -39,6 +43,26 @@ 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
+  public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
+
+  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
+
+  public void addPropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  public void removePropertyChangeListener(PropertyChangeListener listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
+  // end of event notification functionality initialisation
+
   String groupName;
 
   String description;
@@ -65,7 +89,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   /**
    * group members
    */
-  private List<SequenceI> sequences = new ArrayList<>();
+  private List<SequenceI> sequences;
 
   /**
    * representative sequence for this group (if any)
@@ -79,11 +103,15 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public ResidueShaderI cs;
 
-  // start column (base 0)
-  int startRes = 0;
+  /**
+   * start column (base 0)
+   */
+  private int startRes = 0;
 
-  // end column (base 0)
-  int endRes = 0;
+  /**
+   * end column (base 0)
+   */
+  private int endRes = 0;
 
   public Color outlineColour = Color.black;
 
@@ -135,6 +163,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     groupName = "JGroup:" + this.hashCode();
     cs = new ResidueShader();
+    sequences = new ArrayList<>();
   }
 
   /**
@@ -186,6 +215,7 @@ public class SequenceGroup implements AnnotatedCollectionI
       displayBoxes = seqsel.displayBoxes;
       displayText = seqsel.displayText;
       colourText = seqsel.colourText;
+
       startRes = seqsel.startRes;
       endRes = seqsel.endRes;
       cs = new ResidueShader((ResidueShader) seqsel.cs);
@@ -215,6 +245,17 @@ public class SequenceGroup implements AnnotatedCollectionI
     }
   }
 
+  /**
+   * Constructor that copies the given list of sequences
+   * 
+   * @param seqs
+   */
+  public SequenceGroup(List<SequenceI> seqs)
+  {
+    this();
+    this.sequences.addAll(seqs);
+  }
+
   public boolean isShowSequenceLogo()
   {
     return showSequenceLogo;
@@ -229,18 +270,10 @@ public class SequenceGroup implements AnnotatedCollectionI
     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)
+      SequenceI seqipos = seqs[ipos] = seq.getSubSequence(startRes,
+              endRes + 1);
+      if (seqipos != null)
       {
-        seqs[ipos].setDescription(seq.getDescription());
-        seqs[ipos].setDBRefs(seq.getDBRefs());
-        seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
-        if (seq.getDatasetSequence() != null)
-        {
-          seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
-        }
-
         if (seq.getAnnotation() != null)
         {
           AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
@@ -252,7 +285,7 @@ public class SequenceGroup implements AnnotatedCollectionI
             if (alann != null)
             {
               boolean found = false;
-              for (int pos = 0; pos < alann.length; pos++)
+              for (int pos = 0, np = alann.length; pos < np; pos++)
               {
                 if (alann[pos] == tocopy)
                 {
@@ -270,7 +303,13 @@ public class SequenceGroup implements AnnotatedCollectionI
             newannot.restrict(startRes, endRes);
             newannot.setSequenceRef(seqs[ipos]);
             newannot.adjustForAlignment();
-            seqs[ipos].addAlignmentAnnotation(newannot);
+            ContactMatrixI cm = seq
+                    .getContactMatrixFor(seq.getAnnotation()[a]);
+            if (cm != null)
+            {
+              seqs[ipos].addContactListFor(newannot, cm);
+            }
+            seqipos.addAlignmentAnnotation(newannot);
           }
         }
         ipos++;
@@ -496,6 +535,8 @@ public class SequenceGroup implements AnnotatedCollectionI
       if (s != null && !sequences.contains(s))
       {
         sequences.add(s);
+        changeSupport.firePropertyChange(SEQ_GROUP_CHANGED,
+                sequences.size() - 1, sequences.size());
       }
 
       if (recalc)
@@ -604,7 +645,8 @@ public class SequenceGroup implements AnnotatedCollectionI
     } catch (java.lang.OutOfMemoryError err)
     {
       // TODO: catch OOM
-      System.out.println("Out of memory loading groups: " + err);
+      jalview.bin.Console
+              .outPrintln("Out of memory loading groups: " + err);
     }
     return upd;
   }
@@ -620,8 +662,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
@@ -641,8 +685,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
@@ -689,6 +735,8 @@ public class SequenceGroup implements AnnotatedCollectionI
     synchronized (sequences)
     {
       sequences.remove(s);
+      changeSupport.firePropertyChange(SEQ_GROUP_CHANGED,
+              sequences.size() + 1, sequences.size());
 
       if (recalc)
       {
@@ -721,11 +769,15 @@ public class SequenceGroup implements AnnotatedCollectionI
   /**
    * Set the first column selected by this group. Runs from 0<=i<N_cols
    * 
-   * @param i
+   * @param newStart
    */
-  public void setStartRes(int i)
+  public void setStartRes(int newStart)
   {
-    startRes = i;
+    int before = startRes;
+    startRes = Math.max(0, newStart); // sanity check for negative start column
+                                      // positions
+    changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, startRes);
+
   }
 
   /**
@@ -735,7 +787,9 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public void setEndRes(int i)
   {
+    int before = endRes;
     endRes = i;
+    changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, endRes);
   }
 
   /**
@@ -1154,9 +1208,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
         {
@@ -1282,38 +1337,16 @@ public class SequenceGroup implements AnnotatedCollectionI
   @Override
   public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
   {
-    List<AlignmentAnnotation> aa = new ArrayList<>();
-    if (calcId == null)
-    {
-      return aa;
-    }
-    for (AlignmentAnnotation a : getAlignmentAnnotation())
-    {
-      if (calcId.equals(a.getCalcId()))
-      {
-        aa.add(a);
-      }
-    }
-    return aa;
+    return AlignmentAnnotation.findAnnotation(
+            Arrays.asList(getAlignmentAnnotation()), calcId);
   }
 
   @Override
   public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
           String calcId, String label)
   {
-    ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
-    for (AlignmentAnnotation ann : getAlignmentAnnotation())
-    {
-      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);
-      }
-    }
-    return aa;
+    return AlignmentAnnotation.findAnnotations(
+            Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
   }
 
   /**
@@ -1324,17 +1357,8 @@ public class SequenceGroup implements AnnotatedCollectionI
    */
   public boolean hasAnnotation(String calcId)
   {
-    if (calcId != null && !"".equals(calcId))
-    {
-      for (AlignmentAnnotation a : getAlignmentAnnotation())
-      {
-        if (a.getCalcId() == calcId)
-        {
-          return true;
-        }
-      }
-    }
-    return false;
+    return AlignmentAnnotation
+            .hasAnnotation(Arrays.asList(getAlignmentAnnotation()), calcId);
   }
 
   /**
@@ -1344,7 +1368,10 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     synchronized (sequences)
     {
+      int before = sequences.size();
       sequences.clear();
+      changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before,
+              sequences.size());
     }
   }
 
@@ -1432,7 +1459,8 @@ public class SequenceGroup implements AnnotatedCollectionI
   @Override
   public boolean isNucleotide()
   {
-    if (context != null) {
+    if (context != null)
+    {
       return context.isNucleotide();
     }
     return false;
@@ -1457,4 +1485,51 @@ public class SequenceGroup implements AnnotatedCollectionI
   {
     return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
   }
+
+  ////
+  //// Contact Matrix Holder Boilerplate
+  ////
+  ContactMapHolder cmholder = new ContactMapHolder();
+
+  @Override
+  public Collection<ContactMatrixI> getContactMaps()
+  {
+    return cmholder.getContactMaps();
+  }
+
+  @Override
+  public ContactMatrixI getContactMatrixFor(AlignmentAnnotation ann)
+  {
+    return cmholder.getContactMatrixFor(ann);
+  }
+
+  @Override
+  public ContactListI getContactListFor(AlignmentAnnotation _aa, int column)
+  {
+    return cmholder.getContactListFor(_aa, column);
+  }
+
+  @Override
+  public AlignmentAnnotation addContactList(ContactMatrixI cm)
+  {
+    AlignmentAnnotation aa = cmholder.addContactList(cm);
+
+    Annotation _aa[] = new Annotation[getWidth()];
+    Annotation dummy = new Annotation(0.0f);
+    for (int i = 0; i < _aa.length; _aa[i++] = dummy)
+    {
+      ;
+    }
+    aa.annotations = _aa;
+    // TODO passing annotations back to context to be added
+    return aa;
+  }
+
+  @Override
+  public void addContactListFor(AlignmentAnnotation annotation,
+          ContactMatrixI cm)
+  {
+    cmholder.addContactListFor(annotation, cm);
+  }
+
 }