recover original data for tree and pca as alignment view.
[jalview.git] / src / jalview / datamodel / CigarBase.java
index 2306327..31aae1f 100644 (file)
@@ -84,7 +84,7 @@ public abstract class CigarBase
           }
           if (reference!=null) {
             int sbend = cursor+range[i];
-            if (sbend>=rlength) {
+            if (sbend>rlength) {
               sq.append(reference.substring(cursor, rlength));
               while (sbend-- >= rlength)
               {
@@ -183,7 +183,7 @@ public abstract class CigarBase
   }
 
   /**
-   * inefficient add one operation to cigar string
+   * add an operation to cigar string
    * @param op char
    * @param range int
    */
@@ -229,22 +229,206 @@ public abstract class CigarBase
    * NOTE: Insertion operations simply extend width of cigar result - affecting registration of alignment
    * Deletion ops will shorten length of result - and affect registration of alignment
    * Match ops will also affect length of result - affecting registration of alignment
-   * (ie "10M".insert(4,I,3)->"4M3I3M")
-   * (ie "10M".insert(4,D,3)->"4M3D3M")
-   * (ie "5I5M".insert(4,I,3)->"8I5M")
-   * (ie "5I5M".insert(4,D,3)->"4I3M")
-   * if pos is beyond width - I operations are added before the operation
-   * (ie "10M".insert(4,M,3)->"13M")
+   * (ie "10M".insert(4,I,3)->"4M3I3M") - (replace?)
+   * (ie "10M".insert(4,D,3)->"4M3D3M") - (shortens alignment)
+   * (ie "5I5M".insert(4,I,3)->"8I5M") - real insertion
+   * (ie "5I5M".insert(4,D,3)->"4I2D3M") - shortens aligment - I's are removed, Ms changed to Ds
+   * (ie "10M".insert(4,M,3)->"13M")  - lengthens - Is changed to M, Ds changed to M.
    * (ie "5I5M".insert(4,M,3)->"4I8M") - effectively shifts sequence left by 1 residue and extends it by 3
-   * @param pos int
+   * ( "10D5M".insert(-1,M,3)->"3M7D5M")
+   * ( "10D5M".insert(0,M,3)->"7D8M")
+   * ( "10D5M".insert(1,M,3)->"10D8M")
+   *
+   * ( "1M10D5M".insert(0,M,3)->"1M10D8M")
+   * ( "1M10D5M".insert(1,M,3)->"
+   *
+   * if pos is beyond width - I operations are added before the operation
+   * @param pos int -1, 0-length of visible region, or greater to append new ops (with insertions in between)
    * @param op char
    * @param range int
-   */
   public void addOperationAt(int pos, char op, int range)
   {
+    int cursor = -1; // mark the position for the current operation being edited.
+    int o = 0;
+    boolean last_d = false; // previous op was a deletion.
+    if (pos < -1)
+      throw new Error("pos<-1 is not supported.");
+    while (o<length) {
+      if (operation[o] != D)
+      {
+        if ( (cursor + this.range[o]) < pos)
+        {
+          cursor += this.range[o];
+          o++;
+          last_d=false;
+        }
+        else
+        {
+          break;
+        }
+      }
+      else {
+        last_d=true;
+        o++;
+      }
+    }
+    if (o==length) {
+      // must insert more operations before pos
+      if (pos-cursor>0)
+        addInsertion(pos-cursor);
+      // then just add the new operation. Regardless of what it is.
+      addOperation(op, range);
+    } else {
+      int diff = pos - cursor;
 
+      int e_length = length-o; // new edit operation array length.
+      // diff<0 - can only happen before first insertion or match. - affects op and all following
+      // dif==0 - only when at first position of existing op -
+      // diff>0 - must preserve some existing operations
+      int[] e_range = new int[e_length];
+      System.arraycopy(this.range, o, e_range, 0, e_length);
+      char[] e_op = new char[e_length];
+      System.arraycopy(this.operation, o, e_op, 0, e_length);
+      length = o; // can now use add_operation to extend list.
+      int e_o=0; // current operation being edited.
+      switch (op) {
+        case M:
+          switch (e_op[e_o])
+          {
+            case M:
+              if (last_d && diff <= 0)
+              {
+                // reduce D's, if possible
+                if (range<=this.range[o-1]) {
+                  this.range[o - 1] -= range;
+                } else {
+                  this.range[o-1]=0;
+                }
+                if (this.range[o-1]==0)
+                  o--; // lose this op.
+              }
+              e_range[e_o] += range; // just add more matched residues
+              break;
+            case I:
+              // change from insertion to match
+              if (last_d && diff<=0)
+              {
+                // reduce D's, if possible
+                if (range<=this.range[o-1]) {
+                  this.range[o - 1] -= range;
+                } else {
+                  this.range[o-1]=0;
+                }
+                if (this.range[o-1]==0)
+                  o--; // lose this op.
+              }
+              e_range[e_o]
+                    break;
+                default:
+                  throw new Inp
+                      }
+
+                      break;
+                case I:
+                  break;
+                case D:
+              }
+          break;
+        default:
+          throw new Error("Implementation Error: Unknown operation in addOperation!");
+      }
+      // finally, add remaining ops.
+      while (e_o<e_length) {
+        addOperation(e_op[e_o], e_range[e_o]);
+        e_o++;
+      }
+    }
   }
+**/
+  /**
+   * Mark residues from start to end (inclusive) as deleted from the alignment, and removes any insertions.
+   * @param start int
+   * @param end int
+   */
+  public void deleteRange(int start, int end) {
+    if (length==0) {
+      // nothing to do here
+      return;
+    }
+    if (start<0 || start>end)
+      throw new Error("Implementation Error: deleteRange out of bounds: start must be non-negative and less than end.");
+    // find beginning
+    int cursor = 0; // mark the position for the current operation being edited.
+    int rlength=1+end-start; // number of positions to delete
+    int oldlen=length;
+    int o = 0;
+    boolean editing=false;
+    char[] oldops = operation;
+    int[] oldrange = range;
+    length=0;
+    operation = null;
+    range = null;
+    while (o<oldlen && cursor<=end && rlength>0) {
+      if (oldops[o] == D) {
+        // absorbed into new deleted region.
+        addDeleted(oldrange[o++]);
+        continue;
+      }
 
+      int remain = oldrange[o]; // number of op characters left to edit
+      if (!editing) {
+        if ( (cursor + remain) <= start)
+        {
+          addOperation(oldops[o],oldrange[o]);
+          cursor+=oldrange[o++];
+          continue; // next operation
+        }
+        editing=true;
+        // add operations before hidden region
+        if (start-cursor>0) {
+          addOperation(oldops[o], start- cursor);
+          remain -= start - cursor;
+        }
+      }
+      // start inserting new ops
+      if (o<oldlen && editing && rlength>0 && remain>0) {
+        switch (oldops[o]) {
+          case M:
+            if (rlength>remain) {
+              addDeleted(remain);
+            } else {
+              addDeleted(rlength);
+              if (remain-rlength>0)
+                this.addOperation(M,remain-rlength); // add remaining back.
+              rlength=0;
+              remain=0;
+            }
+            break;
+          case I:
+            if (remain-rlength>0) {
+              // only remove some gaps
+              addInsertion(remain-rlength);
+              rlength=0;
+            }
+            break;
+          case D:
+            throw new Error("Implementation error."); // do nothing;
+          default:
+            throw new Error("Implementation Error! Unknown operation '"+oldops[o]+"'");
+        }
+        rlength-=remain;
+        remain = oldrange[++o]; // number of op characters left to edit
+      }
+    }
+    // add remaining
+    while (o<oldlen) {
+      addOperation(oldops[o],oldrange[o++]);
+    }
+    //if (cursor<(start+1)) {
+      // ran out of ops - nothing to do here ?
+     // addInsertion(start-cursor);
+    //}
+  }
   /**
    * sum of ranges in cigar string
    * @return int number of residues hidden, matched, or gaps inserted into sequence
@@ -305,7 +489,6 @@ public abstract class CigarBase
    * @param start alignment column
    * @param end alignment column
    * @return boolean true if residues were marked as deleted.
-   */
   public boolean deleteRange(int start, int end)
   {
     boolean deleted = false;
@@ -358,7 +541,7 @@ public abstract class CigarBase
     }
     return deleted;
   }
-
+*/
   /**
    * Return an ENSEMBL style cigar string where D may indicates excluded parts of seq
    * @return String of form ([0-9]+[IMD])+