JAL-2526 Sequence.findPositions to get residue positions for column
[jalview.git] / src / jalview / datamodel / Sequence.java
index 21f7830..dd7c24a 100755 (executable)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,6 +22,8 @@ package jalview.datamodel;
 
 import jalview.analysis.AlignSeq;
 import jalview.api.DBRefEntryI;
+import jalview.datamodel.features.SequenceFeatures;
+import jalview.datamodel.features.SequenceFeaturesI;
 import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
@@ -34,6 +36,8 @@ import java.util.Enumeration;
 import java.util.List;
 import java.util.Vector;
 
+import com.stevesoft.pat.Regex;
+
 import fr.orsay.lri.varna.models.rna.RNA;
 
 /**
@@ -45,6 +49,11 @@ import fr.orsay.lri.varna.models.rna.RNA;
  */
 public class Sequence extends ASequence implements SequenceI
 {
+  private static final Regex limitrx = new Regex(
+          "[/][0-9]{1,}[-][0-9]{1,}$");
+
+  private static final Regex endrx = new Regex("[0-9]{1,}$");
+
   SequenceI datasetSequence;
 
   String name;
@@ -78,8 +87,7 @@ public class Sequence extends ASequence implements SequenceI
    */
   int index = -1;
 
-  /** array of sequence features - may not be null for a valid sequence object */
-  public SequenceFeature[] sequenceFeatures;
+  private SequenceFeatures sequenceFeatureStore;
 
   /**
    * Creates a new Sequence object.
@@ -96,11 +104,13 @@ public class Sequence extends ASequence implements SequenceI
    */
   public Sequence(String name, String sequence, int start, int end)
   {
+    this();
     initSeqAndName(name, sequence.toCharArray(), start, end);
   }
 
   public Sequence(String name, char[] sequence, int start, int end)
   {
+    this();
     initSeqAndName(name, sequence, start, end);
   }
 
@@ -124,11 +134,6 @@ public class Sequence extends ASequence implements SequenceI
     checkValidRange();
   }
 
-  com.stevesoft.pat.Regex limitrx = new com.stevesoft.pat.Regex(
-          "[/][0-9]{1,}[-][0-9]{1,}$");
-
-  com.stevesoft.pat.Regex endrx = new com.stevesoft.pat.Regex("[0-9]{1,}$");
-
   void parseId()
   {
     if (name == null)
@@ -175,6 +180,14 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
+   * default constructor
+   */
+  private Sequence()
+  {
+    sequenceFeatureStore = new SequenceFeatures();
+  }
+
+  /**
    * Creates a new Sequence object.
    * 
    * @param name
@@ -213,8 +226,8 @@ public class Sequence extends ASequence implements SequenceI
    */
   public Sequence(SequenceI seq, AlignmentAnnotation[] alAnnotation)
   {
+    this();
     initSeqFrom(seq, alAnnotation);
-
   }
 
   /**
@@ -230,33 +243,39 @@ public class Sequence extends ASequence implements SequenceI
   protected void initSeqFrom(SequenceI seq,
           AlignmentAnnotation[] alAnnotation)
   {
-    {
-      char[] oseq = seq.getSequence();
-      initSeqAndName(seq.getName(), Arrays.copyOf(oseq, oseq.length),
-              seq.getStart(), seq.getEnd());
-    }
+    char[] oseq = seq.getSequence();
+    initSeqAndName(seq.getName(), Arrays.copyOf(oseq, oseq.length),
+            seq.getStart(), seq.getEnd());
+
     description = seq.getDescription();
     if (seq != datasetSequence)
     {
       setDatasetSequence(seq.getDatasetSequence());
     }
-    if (datasetSequence == null && seq.getDBRefs() != null)
+    
+    /*
+     * only copy DBRefs and seqfeatures if we really are a dataset sequence
+     */
+    if (datasetSequence == null)
     {
-      // only copy DBRefs and seqfeatures if we really are a dataset sequence
-      DBRefEntry[] dbr = seq.getDBRefs();
-      for (int i = 0; i < dbr.length; i++)
-      {
-        addDBRef(new DBRefEntry(dbr[i]));
-      }
-      if (seq.getSequenceFeatures() != null)
+      if (seq.getDBRefs() != null)
       {
-        SequenceFeature[] sf = seq.getSequenceFeatures();
-        for (int i = 0; i < sf.length; i++)
+        DBRefEntry[] dbr = seq.getDBRefs();
+        for (int i = 0; i < dbr.length; i++)
         {
-          addSequenceFeature(new SequenceFeature(sf[i]));
+          addDBRef(new DBRefEntry(dbr[i]));
         }
       }
+
+      /*
+       * make copies of any sequence features
+       */
+      for (SequenceFeature sf : seq.getSequenceFeatures())
+      {
+        addSequenceFeature(new SequenceFeature(sf));
+      }
     }
+
     if (seq.getAnnotation() != null)
     {
       AlignmentAnnotation[] sqann = seq.getAnnotation();
@@ -293,122 +312,67 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   @Override
-  public void setSequenceFeatures(SequenceFeature[] features)
+  public void setSequenceFeatures(List<SequenceFeature> features)
   {
-    if (datasetSequence == null)
-    {
-      sequenceFeatures = features;
-    }
-    else
+    if (datasetSequence != null)
     {
-      if (datasetSequence.getSequenceFeatures() != features
-              && datasetSequence.getSequenceFeatures() != null
-              && datasetSequence.getSequenceFeatures().length > 0)
-      {
-        new Exception(
-                "Warning: JAL-2046 side effect ? Possible implementation error: overwriting dataset sequence features by setting sequence features on alignment")
-                .printStackTrace();
-      }
       datasetSequence.setSequenceFeatures(features);
+      return;
     }
+    sequenceFeatureStore = new SequenceFeatures(features);
   }
 
   @Override
-  public synchronized void addSequenceFeature(SequenceFeature sf)
+  public synchronized boolean addSequenceFeature(SequenceFeature sf)
   {
-    if (sequenceFeatures == null && datasetSequence != null)
-    {
-      datasetSequence.addSequenceFeature(sf);
-      return;
-    }
-    if (sequenceFeatures == null)
+    if (sf.getType() == null)
     {
-      sequenceFeatures = new SequenceFeature[0];
+      System.err.println("SequenceFeature type may not be null: "
+              + sf.toString());
+      return false;
     }
 
-    for (int i = 0; i < sequenceFeatures.length; i++)
+    if (datasetSequence != null)
     {
-      if (sequenceFeatures[i].equals(sf))
-      {
-        return;
-      }
+      return datasetSequence.addSequenceFeature(sf);
     }
 
-    SequenceFeature[] temp = new SequenceFeature[sequenceFeatures.length + 1];
-    System.arraycopy(sequenceFeatures, 0, temp, 0, sequenceFeatures.length);
-    temp[sequenceFeatures.length] = sf;
-
-    sequenceFeatures = temp;
+    return sequenceFeatureStore.add(sf);
   }
 
   @Override
   public void deleteFeature(SequenceFeature sf)
   {
-    if (sequenceFeatures == null)
-    {
-      if (datasetSequence != null)
-      {
-        datasetSequence.deleteFeature(sf);
-      }
-      return;
-    }
-
-    int index = 0;
-    for (index = 0; index < sequenceFeatures.length; index++)
-    {
-      if (sequenceFeatures[index].equals(sf))
-      {
-        break;
-      }
-    }
-
-    if (index == sequenceFeatures.length)
-    {
-      return;
-    }
-
-    int sfLength = sequenceFeatures.length;
-    if (sfLength < 2)
+    if (datasetSequence != null)
     {
-      sequenceFeatures = null;
+      datasetSequence.deleteFeature(sf);
     }
     else
     {
-      SequenceFeature[] temp = new SequenceFeature[sfLength - 1];
-      System.arraycopy(sequenceFeatures, 0, temp, 0, index);
-
-      if (index < sfLength)
-      {
-        System.arraycopy(sequenceFeatures, index + 1, temp, index,
-                sequenceFeatures.length - index - 1);
-      }
-
-      sequenceFeatures = temp;
+      sequenceFeatureStore.delete(sf);
     }
   }
 
   /**
-   * Returns the sequence features (if any), looking first on the sequence, then
-   * on its dataset sequence, and so on until a non-null value is found (or
-   * none). This supports retrieval of sequence features stored on the sequence
-   * (as in the applet) or on the dataset sequence (as in the Desktop version).
+   * {@inheritDoc}
    * 
    * @return
    */
   @Override
-  public SequenceFeature[] getSequenceFeatures()
+  public List<SequenceFeature> getSequenceFeatures()
   {
-    SequenceFeature[] features = sequenceFeatures;
-
-    SequenceI seq = this;
-    int count = 0; // failsafe against loop in sequence.datasetsequence...
-    while (features == null && seq.getDatasetSequence() != null
-            && count++ < 10)
+    if (datasetSequence != null)
     {
-      seq = seq.getDatasetSequence();
-      features = ((Sequence) seq).sequenceFeatures;
+      return datasetSequence.getSequenceFeatures();
     }
-    return features;
+    return sequenceFeatureStore.getAllFeatures();
+  }
+
+  @Override
+  public SequenceFeaturesI getFeatures()
+  {
+    return datasetSequence != null ? datasetSequence.getFeatures()
+            : sequenceFeatureStore;
   }
 
   @Override
@@ -698,7 +662,7 @@ public class Sequence extends ASequence implements SequenceI
     // Rely on end being at least as long as the length of the sequence.
     while ((i < sequence.length) && (j <= end) && (j <= pos))
     {
-      if (!jalview.util.Comparison.isGap(sequence[i]))
+      if (!Comparison.isGap(sequence[i]))
       {
         j++;
       }
@@ -724,7 +688,7 @@ public class Sequence extends ASequence implements SequenceI
     int seqlen = sequence.length;
     while ((j < i) && (j < seqlen))
     {
-      if (!jalview.util.Comparison.isGap(sequence[j]))
+      if (!Comparison.isGap(sequence[j]))
       {
         pos++;
       }
@@ -736,6 +700,68 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Range findPositions(int fromCol, int toCol)
+  {
+    /*
+     * count residues before fromCol
+     */
+    int j = 0;
+    int count = 0;
+    int seqlen = sequence.length;
+    while (j < fromCol && j < seqlen)
+    {
+      if (!Comparison.isGap(sequence[j]))
+      {
+        count++;
+      }
+      j++;
+    }
+
+    /*
+     * find first and last residues between fromCol and toCol
+     */
+    int firstPos = 0;
+    int lastPos = 0;
+    int firstPosCol = 0;
+    boolean foundFirst = false;
+    
+    while (j <= toCol && j < seqlen)
+    {
+      if (!Comparison.isGap(sequence[j]))
+      {
+        count++;
+        if (!foundFirst)
+        {
+          firstPos = count;
+          firstPosCol = j;
+          foundFirst = true;
+        }
+        lastPos = count;
+      }
+      j++;
+    }
+
+    if (firstPos == 0)
+    {
+      /*
+       * no residues in this range
+       */
+      return null;
+    }
+
+    /*
+     * adjust for sequence start coordinate
+     */
+    firstPos += start - 1;
+    lastPos += start - 1;
+
+    return new Range(firstPos, lastPos);
+  }
+
+  /**
    * Returns an int array where indices correspond to each residue in the
    * sequence and the element value gives its position in the alignment
    * 
@@ -1154,8 +1180,8 @@ public class Sequence extends ASequence implements SequenceI
 
       dsseq.setDescription(description);
       // move features and database references onto dataset sequence
-      dsseq.sequenceFeatures = sequenceFeatures;
-      sequenceFeatures = null;
+      dsseq.sequenceFeatureStore = sequenceFeatureStore;
+      sequenceFeatureStore = null;
       dsseq.dbrefs = dbrefs;
       dbrefs = null;
       // TODO: search and replace any references to this sequence with
@@ -1214,11 +1240,11 @@ public class Sequence extends ASequence implements SequenceI
       return null;
     }
 
-    Vector subset = new Vector();
-    Enumeration e = annotation.elements();
+    Vector<AlignmentAnnotation> subset = new Vector<AlignmentAnnotation>();
+    Enumeration<AlignmentAnnotation> e = annotation.elements();
     while (e.hasMoreElements())
     {
-      AlignmentAnnotation ann = (AlignmentAnnotation) e.nextElement();
+      AlignmentAnnotation ann = e.nextElement();
       if (ann.label != null && ann.label.equals(label))
       {
         subset.addElement(ann);
@@ -1233,7 +1259,7 @@ public class Sequence extends ASequence implements SequenceI
     e = subset.elements();
     while (e.hasMoreElements())
     {
-      anns[i++] = (AlignmentAnnotation) e.nextElement();
+      anns[i++] = e.nextElement();
     }
     subset.removeAllElements();
     return anns;
@@ -1286,12 +1312,12 @@ public class Sequence extends ASequence implements SequenceI
     if (entry.getSequenceFeatures() != null)
     {
 
-      SequenceFeature[] sfs = entry.getSequenceFeatures();
-      for (int si = 0; si < sfs.length; si++)
+      List<SequenceFeature> sfs = entry.getSequenceFeatures();
+      for (SequenceFeature feature : sfs)
       {
-        SequenceFeature sf[] = (mp != null) ? mp.locateFeature(sfs[si])
-                : new SequenceFeature[] { new SequenceFeature(sfs[si]) };
-        if (sf != null && sf.length > 0)
+        SequenceFeature sf[] = (mp != null) ? mp.locateFeature(feature)
+                : new SequenceFeature[] { new SequenceFeature(feature) };
+        if (sf != null)
         {
           for (int sfi = 0; sfi < sf.length; sfi++)
           {
@@ -1304,10 +1330,10 @@ public class Sequence extends ASequence implements SequenceI
     // transfer PDB entries
     if (entry.getAllPDBEntries() != null)
     {
-      Enumeration e = entry.getAllPDBEntries().elements();
+      Enumeration<PDBEntry> e = entry.getAllPDBEntries().elements();
       while (e.hasMoreElements())
       {
-        PDBEntry pdb = (PDBEntry) e.nextElement();
+        PDBEntry pdb = e.nextElement();
         addPDBId(pdb);
       }
     }
@@ -1475,4 +1501,17 @@ public class Sequence extends ASequence implements SequenceI
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public List<SequenceFeature> findFeatures(int from, int to,
+          String... types)
+  {
+    if (datasetSequence != null)
+    {
+      return datasetSequence.findFeatures(from, to, types);
+    }
+    return sequenceFeatureStore.findFeatures(from, to, types);
+  }
 }