JAL-3140 confine intervalstore imports to jalview.datamodel.features
[jalview.git] / src / jalview / datamodel / Sequence.java
index 933c89d..37e3278 100755 (executable)
@@ -34,6 +34,7 @@ import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Vector;
@@ -77,11 +78,6 @@ public class Sequence extends ASequence implements SequenceI
    */
   Vector<AlignmentAnnotation> annotation;
 
-  /**
-   * The index of the sequence in a MSA
-   */
-  int index = -1;
-
   private SequenceFeaturesI sequenceFeatureStore;
 
   /*
@@ -449,7 +445,7 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public Vector<PDBEntry> getAllPDBEntries()
   {
-    return pdbIds == null ? new Vector<PDBEntry>() : pdbIds;
+    return pdbIds == null ? new Vector<>() : pdbIds;
   }
 
   /**
@@ -662,10 +658,10 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
-   * DOCUMENT ME!
+   * Sets the sequence description, and also parses out any special formats of
+   * interest
    * 
    * @param desc
-   *          DOCUMENT ME!
    */
   @Override
   public void setDescription(String desc)
@@ -673,10 +669,67 @@ public class Sequence extends ASequence implements SequenceI
     this.description = desc;
   }
 
+  @Override
+  public void setGeneLoci(String speciesId, String assemblyId,
+          String chromosomeId, MapList map)
+  {
+    addDBRef(new DBRefEntry(speciesId, assemblyId, DBRefEntry.CHROMOSOME
+            + ":" + chromosomeId, new Mapping(map)));
+  }
+
   /**
-   * DOCUMENT ME!
+   * Returns the gene loci mapping for the sequence (may be null)
    * 
-   * @return DOCUMENT ME!
+   * @return
+   */
+  @Override
+  public GeneLociI getGeneLoci()
+  {
+    DBRefEntry[] refs = getDBRefs();
+    if (refs != null)
+    {
+      for (final DBRefEntry ref : refs)
+      {
+        if (ref.isChromosome())
+        {
+          return new GeneLociI()
+          {
+            @Override
+            public String getSpeciesId()
+            {
+              return ref.getSource();
+            }
+
+            @Override
+            public String getAssemblyId()
+            {
+              return ref.getVersion();
+            }
+
+            @Override
+            public String getChromosomeId()
+            {
+              // strip off "chromosome:" prefix to chrId
+              return ref.getAccessionId().substring(
+                      DBRefEntry.CHROMOSOME.length() + 1);
+            }
+
+            @Override
+            public MapList getMap()
+            {
+              return ref.getMap().getMap();
+            }
+          };
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Answers the description
+   * 
+   * @return
    */
   @Override
   public String getDescription()
@@ -762,7 +815,7 @@ public class Sequence extends ASequence implements SequenceI
    * @param curs
    * @return
    */
-  protected int findIndex(int pos, SequenceCursor curs)
+  protected int findIndex(final int pos, SequenceCursor curs)
   {
     if (!isValidCursor(curs))
     {
@@ -788,8 +841,13 @@ public class Sequence extends ASequence implements SequenceI
     while (newPos != pos)
     {
       col += delta; // shift one column left or right
-      if (col < 0 || col == sequence.length)
+      if (col < 0)
+      {
+        break;
+      }
+      if (col == sequence.length)
       {
+        col--; // return last column if we failed to reach pos
         break;
       }
       if (!Comparison.isGap(sequence[col]))
@@ -799,7 +857,14 @@ public class Sequence extends ASequence implements SequenceI
     }
 
     col++; // convert back to base 1
-    updateCursor(pos, col, curs.firstColumnPosition);
+
+    /*
+     * only update cursor if we found the target position
+     */
+    if (newPos == pos)
+    {
+      updateCursor(pos, col, curs.firstColumnPosition);
+    }
 
     return col;
   }
@@ -1004,7 +1069,7 @@ public class Sequence extends ASequence implements SequenceI
    * {@inheritDoc}
    */
   @Override
-  public Range findPositions(int fromColumn, int toColumn)
+  public ContiguousI findPositions(int fromColumn, int toColumn)
   {
     if (toColumn < fromColumn || fromColumn < 1)
     {
@@ -1186,7 +1251,7 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   @Override
-  public void deleteChars(int i, int j)
+  public void deleteChars(final int i, final int j)
   {
     int newstart = start, newend = end;
     if (i >= sequence.length || i < 0)
@@ -1198,62 +1263,75 @@ public class Sequence extends ASequence implements SequenceI
     boolean createNewDs = false;
     // TODO: take a (second look) at the dataset creation validation method for
     // the very large sequence case
-    int eindex = -1, sindex = -1;
-    boolean ecalc = false, scalc = false;
+    int startIndex = findIndex(start) - 1;
+    int endIndex = findIndex(end) - 1;
+    int startDeleteColumn = -1; // for dataset sequence deletions
+    int deleteCount = 0;
+
     for (int s = i; s < j; s++)
     {
-      if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
+      if (Comparison.isGap(sequence[s]))
+      {
+        continue;
+      }
+      deleteCount++;
+      if (startDeleteColumn == -1)
+      {
+        startDeleteColumn = findPosition(s) - start;
+      }
+      if (createNewDs)
       {
-        if (createNewDs)
+        newend--;
+      }
+      else
+      {
+        if (startIndex == s)
         {
-          newend--;
+          /*
+           * deleting characters from start of sequence; new start is the
+           * sequence position of the next column (position to the right
+           * if the column position is gapped)
+           */
+          newstart = findPosition(j);
+          break;
         }
         else
         {
-          if (!scalc)
-          {
-            sindex = findIndex(start) - 1;
-            scalc = true;
-          }
-          if (sindex == s)
+          if (endIndex < j)
           {
-            // delete characters including start of sequence
-            newstart = findPosition(j);
-            break; // don't need to search for any more residue characters.
+            /*
+             * deleting characters at end of sequence; new end is the sequence
+             * position of the column before the deletion; subtract 1 if this is
+             * gapped since findPosition returns the next sequence position
+             */
+            newend = findPosition(i - 1);
+            if (Comparison.isGap(sequence[i - 1]))
+            {
+              newend--;
+            }
+            break;
           }
           else
           {
-            // delete characters after start.
-            if (!ecalc)
-            {
-              eindex = findIndex(end) - 1;
-              ecalc = true;
-            }
-            if (eindex < j)
-            {
-              // delete characters at end of sequence
-              newend = findPosition(i - 1);
-              break; // don't need to search for any more residue characters.
-            }
-            else
-            {
-              createNewDs = true;
-              newend--; // decrease end position by one for the deleted residue
-              // and search further
-            }
+            createNewDs = true;
+            newend--;
           }
         }
       }
     }
-    // deletion occured in the middle of the sequence
+
     if (createNewDs && this.datasetSequence != null)
     {
-      // construct a new sequence
+      /*
+       * if deletion occured in the middle of the sequence,
+       * construct a new dataset sequence and delete the residues
+       * that were deleted from the aligned sequence
+       */
       Sequence ds = new Sequence(datasetSequence);
+      ds.deleteChars(startDeleteColumn, startDeleteColumn + deleteCount);
+      datasetSequence = ds;
       // TODO: remove any non-inheritable properties ?
       // TODO: create a sequence mapping (since there is a relation here ?)
-      ds.deleteChars(i, j);
-      datasetSequence = ds;
     }
     start = newstart;
     end = newend;
@@ -1703,30 +1781,6 @@ public class Sequence extends ASequence implements SequenceI
     }
   }
 
-  /**
-   * @return The index (zero-based) on this sequence in the MSA. It returns
-   *         {@code -1} if this information is not available.
-   */
-  @Override
-  public int getIndex()
-  {
-    return index;
-  }
-
-  /**
-   * Defines the position of this sequence in the MSA. Use the value {@code -1}
-   * if this information is undefined.
-   * 
-   * @param The
-   *          position for this sequence. This value is zero-based (zero for
-   *          this first sequence)
-   */
-  @Override
-  public void setIndex(int value)
-  {
-    index = value;
-  }
-
   @Override
   public void setRNA(RNA r)
   {
@@ -1941,4 +1995,73 @@ public class Sequence extends ASequence implements SequenceI
 
     return count;
   }
+
+  @Override
+  public String getSequenceStringFromIterator(Iterator<int[]> it)
+  {
+    StringBuilder newSequence = new StringBuilder();
+    while (it.hasNext())
+    {
+      int[] block = it.next();
+      if (it.hasNext())
+      {
+        newSequence.append(getSequence(block[0], block[1] + 1));
+      }
+      else
+      {
+        newSequence.append(getSequence(block[0], block[1]));
+      }
+    }
+
+    return newSequence.toString();
+  }
+
+  @Override
+  public int firstResidueOutsideIterator(Iterator<int[]> regions)
+  {
+    int start = 0;
+
+    if (!regions.hasNext())
+    {
+      return findIndex(getStart()) - 1;
+    }
+
+    // Simply walk along the sequence whilst watching for region
+    // boundaries
+    int hideStart = getLength();
+    int hideEnd = -1;
+    boolean foundStart = false;
+
+    // step through the non-gapped positions of the sequence
+    for (int i = getStart(); i <= getEnd() && (!foundStart); i++)
+    {
+      // get alignment position of this residue in the sequence
+      int p = findIndex(i) - 1;
+
+      // update region start/end
+      while (hideEnd < p && regions.hasNext())
+      {
+        int[] region = regions.next();
+        hideStart = region[0];
+        hideEnd = region[1];
+      }
+      if (hideEnd < p)
+      {
+        hideStart = getLength();
+      }
+      // update boundary for sequence
+      if (p < hideStart)
+      {
+        start = p;
+        foundStart = true;
+      }
+    }
+
+    if (foundStart)
+    {
+      return start;
+    }
+    // otherwise, sequence was completely hidden
+    return 0;
+  }
 }