recover original data for tree and pca as alignment view.
authorjprocter <Jim Procter>
Fri, 4 Aug 2006 14:28:05 +0000 (14:28 +0000)
committerjprocter <Jim Procter>
Fri, 4 Aug 2006 14:28:05 +0000 (14:28 +0000)
13 files changed:
src/jalview/analysis/NJTree.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentView.java [new file with mode: 0644]
src/jalview/datamodel/CigarArray.java
src/jalview/datamodel/CigarBase.java
src/jalview/datamodel/ColumnSelection.java
src/jalview/datamodel/SeqCigar.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/TreePanel.java
src/jalview/util/ShiftList.java [new file with mode: 0644]
src/jalview/ws/MsaWSClient.java

index 974175f..674af7f 100755 (executable)
@@ -42,7 +42,7 @@ public class NJTree
 \r
     //SequenceData is a string representation of what the user\r
     //sees. The display may contain hidden columns.\r
-    CigarArray seqData=null;\r
+    public AlignmentView seqData=null;\r
 \r
     int[] done;\r
     int noseqs;\r
@@ -76,7 +76,7 @@ public class NJTree
      * @param odata Cigar[]\r
      * @param treefile NewickFile\r
      */\r
-    public NJTree(SequenceI[] seqs, CigarArray odata, NewickFile treefile) {\r
+    public NJTree(SequenceI[] seqs, AlignmentView odata, NewickFile treefile) {\r
       this(seqs, treefile);\r
       if (odata!=null)\r
         seqData = odata;\r
@@ -167,7 +167,7 @@ public class NJTree
      * @param end DOCUMENT ME!\r
      */\r
     public NJTree(SequenceI[] sequence,\r
-                  CigarArray seqData,\r
+                  AlignmentView seqData,\r
                   String type,\r
                   String pwtype,\r
                   int start, int end)\r
@@ -184,8 +184,9 @@ public class NJTree
             {\r
               seqs[i] = new SeqCigar(sequence[i], start, end);\r
             }\r
-            this.seqData = new CigarArray(seqs);\r
-            this.seqData.addOperation(CigarArray.M, end-start+1);\r
+            CigarArray sdata = new CigarArray(seqs);\r
+            sdata.addOperation(CigarArray.M, end-start+1);\r
+            this.seqData = new AlignmentView(sdata);\r
         }\r
 \r
         if (!(type.equals("NJ")))\r
@@ -1033,7 +1034,6 @@ public class NJTree
       }\r
       return sb.toString();\r
     }\r
-\r
     /**\r
      * DOCUMENT ME!\r
      *\r
index 588134a..d67592a 100755 (executable)
@@ -70,11 +70,7 @@ public class Alignment implements AlignmentI
      * @param seqs SeqCigar[]
      */
     public Alignment(SeqCigar[] alseqs) {
-
-      SequenceI[] seqs = new SequenceI[alseqs.length];
-      for (int i=0; i<alseqs.length; i++) {
-        seqs[i] = alseqs[i].getSeq(this.gapCharacter);
-      }
+      SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter, new ColumnSelection());
       initAlignment(seqs);
     }
     /**
@@ -83,7 +79,7 @@ public class Alignment implements AlignmentI
      * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
      * @param compactAlignment CigarArray
      */
-    public Alignment(CigarArray compactAlignment) {
+    public static AlignmentI createAlignment(CigarArray compactAlignment) {
       throw new Error("Alignment(CigarArray) not yet implemented");
       // this(compactAlignment.refCigars);
     }
diff --git a/src/jalview/datamodel/AlignmentView.java b/src/jalview/datamodel/AlignmentView.java
new file mode 100644 (file)
index 0000000..15309b7
--- /dev/null
@@ -0,0 +1,68 @@
+package jalview.datamodel;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2004</p>
+ *
+ * <p>Company: Dundee University</p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class AlignmentView
+{
+    /**
+     * Transient object compactly representing a 'view' of an alignment - with discontinuities marked.
+     */
+    private SeqCigar[] sequences = null;
+  private int[] contigs = null;
+  public AlignmentView(CigarArray seqcigararray)
+  {
+    if (!seqcigararray.isSeqCigarArray())
+      throw new Error("Implementation Error - can only make an alignment view from a CigarArray of sequences.");
+    contigs = seqcigararray.applyDeletions();
+    sequences = seqcigararray.getSeqCigarArray();
+  }
+
+  public void setSequences(SeqCigar[] sequences)
+  {
+    this.sequences = sequences;
+  }
+
+  public void setContigs(int[] contigs)
+  {
+    this.contigs = contigs;
+  }
+
+  public SeqCigar[] getSequences()
+  {
+    return sequences;
+  }
+
+  public int[] getContigs()
+  {
+    return contigs;
+  }
+  public Object[] getAlignmentAndColumnSelection(char gapCharacter) {
+    ColumnSelection colsel = new ColumnSelection();
+
+    return new Object[] { SeqCigar.createAlignmentSequences(sequences, gapCharacter, colsel), colsel};
+  }
+  /**
+   * getSequenceStrings
+   *
+   * @param c char
+   * @return String[]
+   */
+  public String[] getSequenceStrings(char c)
+  {
+    String[] seqs=new String[sequences.length];
+    for (int n=0; n<sequences.length; n++) {
+      seqs[n] = sequences[n].getSequenceString(c);
+    }
+    return seqs;
+  }
+}
index 447b4d0..8961425 100644 (file)
@@ -24,7 +24,6 @@ public class CigarArray extends CigarBase
   {
     return seqcigararray;
   }
-
   /**
    * Apply CIGAR operations to several cigars in parallel
    * will throw an error if any of cigar are actually CigarArrays.
@@ -109,9 +108,67 @@ public class CigarArray extends CigarBase
    * Combines the CigarArray cigar operations with the operations in each
    * reference cigar - creating a new reference cigar
    * @return Cigar[]
-   */
+
   public CigarBase[] getEditedCigars() {
 
     return new CigarBase[] {};
   }
+*/
+  /**
+   * applyDeletions
+   * edits underlying refCigars to propagate deleted regions, and removes deletion
+   * operations from CigarArray operation list.
+   * @return int[] position where deletion occured in cigarArray or null if none occured
+   */
+  public int[] applyDeletions()
+  {
+    java.util.Vector delpos=null;
+    if (length==0)
+      return null;
+    int cursor=0; // range counter for deletions
+    int vcursor=0; // visible column index
+    for (int i=0; i<length; i++) {
+      if (operation[i]!=D) {
+        if (operation[i]==M)
+          cursor+=range[i];
+        vcursor+=range[i];
+      }
+      else
+      {
+        if (delpos==null)
+          delpos=new java.util.Vector();
+        int delstart=cursor, delend=cursor+range[i]-1; // inclusive
+        delpos.add(new Integer(vcursor-1)); // index of left hand column of hidden region boundary
+        System.arraycopy(operation, i+1, operation, i, length-i);
+        System.arraycopy(range, i+1, range, i, length-i);
+        length--;
+        for (int s=0; s<refCigars.length; s++) {
+          refCigars[s].deleteRange(delstart, delend);
+        }
+      }
+    }
+    if (delpos!=null)
+    {
+      int[] pos=new int[delpos.size()];
+      for (int k = 0, l = delpos.size(); k < l; k++) {
+        pos[k] = ((Integer) delpos.get(k)).intValue();
+        delpos.set(k,null);
+      }
+      delpos=null;
+      return pos;
+    }
+    return null;
+  }
+  /**
+   *
+   * @return SeqCigar[] or null if CigarArray is not a SeqCigarArray (ie it does not resolve to set of seqCigars)
+   */
+  public SeqCigar[] getSeqCigarArray() {
+    if (!isSeqCigarArray())
+      return null;
+    SeqCigar[] sa = new SeqCigar[refCigars.length];
+    for (int i=0; i<refCigars.length; i++)
+      sa[i] = (SeqCigar) refCigars[i];
+    return sa;
+  }
 }
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])+
index 4f4a510..d54bb8d 100644 (file)
@@ -168,10 +168,10 @@ public class ColumnSelection
 
 
     /**
-     * DOCUMENT ME!
+     * propagate shift in alignment columns to column selection
      *
-     * @param start DOCUMENT ME!
-     * @param change DOCUMENT ME!
+     * @param start beginning of edit
+     * @param change shift in edit (-ve or +ve number of columns)
      */
     public void compensateForEdit(int start, int change)
     {
index 536e4ea..c9b737d 100644 (file)
 package jalview.datamodel;
 
-import jalview.analysis.AlignSeq;
+import jalview.analysis.*;
+import jalview.util.ShiftList;
+import java.util.Vector;
 
 public class SeqCigar
     extends CigarSimple
 {
-
-  private SequenceI refseq=null;
+    /**
+     * start(inclusive) and end(exclusive) of subsequence on refseq
+     */
+    private int start, end;
+  private SequenceI refseq = null;
   /**
    * Reference dataset sequence for the cigar string
    * @return SequenceI
    */
-  public SequenceI getRefSeq() {
+  public SequenceI getRefSeq()
+  {
     return refseq;
   }
   /**
+   *
+   * @return int start index of cigar ops on refSeq
+   */
+  public int getStart() {
+    return start;
+  }
+  /**
+   *
+   * @return int end index (exclusive) of cigar ops on refSeq
+   */
+  public int getEnd() {
+    return end;
+  }
+  /**
    * Returns sequence as a string with cigar operations applied to it
    * @return String
    */
   public String getSequenceString(char GapChar)
   {
-    return (length==0) ? "" : (String) getSequenceAndDeletions(refseq.getSequence(), GapChar)[0];
+    return (length == 0) ? "" :
+        (String) getSequenceAndDeletions(refseq.getSequence().substring(start, end), GapChar)[0];
   }
 
   /**
    * recreates a gapped and edited version of RefSeq or null for an empty cigar string
    * @return SequenceI
    */
-  public SequenceI getSeq(char GapChar) {
+  public SequenceI getSeq(char GapChar)
+  {
     Sequence seq;
-    if (refseq==null || length==0)
+    if (refseq == null || length == 0)
+    {
       return null;
-    Object[] edit_result=getSequenceAndDeletions(refseq.getSequence(), GapChar);
-    if (edit_result==null)
-      throw new Error("Implementation Error - unexpected null from getSequenceAndDeletions");
+    }
+    Object[] edit_result = getSequenceAndDeletions(refseq.getSequence().substring(start,end),
+        GapChar);
+    if (edit_result == null)
+    {
+      throw new Error(
+          "Implementation Error - unexpected null from getSequenceAndDeletions");
+    }
 
-    seq = new Sequence(refseq.getName(), (String) edit_result[0], refseq.getStart()+((int[]) edit_result[1])[0], refseq.getStart()+((int[]) edit_result[1])[2]);
+    seq = new Sequence(refseq.getName(), (String) edit_result[0],
+                       refseq.getStart() + start+( (int[]) edit_result[1])[0],
+                       refseq.getStart() + start+( (int[]) edit_result[1])[2]);
     seq.setDatasetSequence(refseq);
     return seq;
   }
+
   /*
-  We don't allow this - refseq is given at construction time only
+     We don't allow this - refseq is given at construction time only
    public void setSeq(SequenceI seq) {
     this.seq = seq;
-  }
-  */
+     }
+   */
   /**
    * internal constructor - sets seq to a gapless sequence derived from seq
    * and prepends any 'D' operations needed to get to the first residue of seq.
    * @param seq SequenceI
+   * @param initialDeletion true to mark initial dataset sequence residues as deleted in subsequence
+   * @param _s index of first position in seq
+   * @param _e index after last position in (possibly gapped) seq
    * @return true if gaps are present in seq
    */
-  private boolean _setSeq(SequenceI seq) {
-    boolean hasgaps=false;
-
-    if (seq==null)
-      throw new Error("Implementation Error - _setSeq(null)");
-
-    // Find correct sequence to reference and add initial hidden offset
-    SequenceI ds = seq.getDatasetSequence();
-    if (ds==null) {
-      ds = new Sequence(seq.getName(),
-                        AlignSeq.extractGaps(jalview.util.Comparison.GapChars, new String(seq.getSequence())),
-                        seq.getStart(),
-                        seq.getEnd());
+  private boolean _setSeq(SequenceI seq, boolean initialDeletion, int _s, int _e)
+  {
+    boolean hasgaps = false;
+    if (seq == null)
+    {
+      throw new Error("Implementation Error - _setSeq(null,...)");
     }
-    // check that we haven't just duplicated an ungapped sequence.
-      if (ds.getLength()==seq.getLength()) {
+    if (_s<0)
+      throw new Error("Implementation Error: _s="+_s);
+    String seq_string = seq.getSequence();
+    if (_e==0 || _e<_s || _e>seq_string.length())
+      _e=seq_string.length();
+    // resolve start and end positions relative to ungapped reference sequence
+    start = seq.findPosition(_s)-seq.getStart();
+    end = seq.findPosition(_e)-seq.getStart();
+    int l_ungapped = end-start;
+    // Find correct sequence to reference and correct start and end - if necessary
+    SequenceI ds = seq.getDatasetSequence();
+    if (ds == null)
+    {
+      // make a new dataset sequence
+      String ungapped = AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
+                                               new String(seq_string));
+      l_ungapped=ungapped.length();
+      // check that we haven't just duplicated an ungapped sequence.
+      if (l_ungapped == seq.getLength())
+      {
         ds = seq;
+      }
+      else
+      {
+        ds = new Sequence(seq.getName(), ungapped,
+                          seq.getStart(),
+                          seq.getStart()+ungapped.length()-1);
+        // JBPNote: this would be consistent but may not be useful
+        //        seq.setDatasetSequence(ds);
+      }
+    }
+    // add in offset between seq and the dataset sequence
+    if (ds.getStart() < seq.getStart())
+    {
+      int offset=seq.getStart()-ds.getStart();
+      if (initialDeletion) {
+          // absolute cigar string
+          addDeleted(_s+offset);
+          start=0;
+          end+=offset;
       } else {
-        hasgaps = true;
+          // normal behaviour - just mark start and end subsequence
+          start+=offset;
+          end+=offset;
+
       }
+
+    }
+
+    // any gaps to process ?
+    if (l_ungapped!=(_e-_s))
+      hasgaps=true;
+
     this.refseq = ds;
-    // Adjust offset
-    if (ds.getStart()<seq.getStart()) {
-      addDeleted(seq.getStart()-ds.getStart());
+
+    // Check  offsets
+    if (end>ds.getLength()) {
+      throw new Error("SeqCigar: Possible implementation error: sequence is longer than dataset sequence");
+//      end = ds.getLength();
     }
+
     return hasgaps;
   }
+
   /**
    * directly initialise a cigar object with a sequence of range, operation pairs and a sequence to apply it to.
    * operation and range should be relative to the seq.getStart()'th residue of the dataset seq resolved from seq.
@@ -85,139 +163,184 @@ public class SeqCigar
    * @param operation char[]
    * @param range int[]
    */
-  public SeqCigar(SequenceI seq, char operation[], int range[]) {
+  public SeqCigar(SequenceI seq, char operation[], int range[])
+  {
     super();
-    if (seq==null)
+    if (seq == null)
+    {
       throw new Error("Implementation Bug. Null seq !");
-    if (operation.length!=range.length) {
+    }
+    if (operation.length != range.length)
+    {
       throw new Error("Implementation Bug. Cigar Operation list!= range list");
     }
 
-    if (operation!=null) {
-      this.operation = new char[operation.length+_inc_length];
-      this.range = new int[operation.length+_inc_length];
+    if (operation != null)
+    {
+      this.operation = new char[operation.length + _inc_length];
+      this.range = new int[operation.length + _inc_length];
 
-      if (_setSeq(seq)) {
+      if (_setSeq(seq, false, 0, 0))
+      {
         throw new Error("NOT YET Implemented: Constructing a Cigar object from a cigar string and a gapped sequence.");
       }
-      for (int i = this.length, j=0; j < operation.length; i++,j++)
+      for (int i = this.length, j = 0; j < operation.length; i++, j++)
       {
         char op = operation[j];
         if (op != M && op != I && op != D)
         {
           throw new Error(
-              "Implementation Bug. Cigar Operation '"+j+"' '"+op+"' not one of '"+M+"', '"+I+"', or '"+D+"'.");
+              "Implementation Bug. Cigar Operation '" + j + "' '" + op +
+              "' not one of '" + M + "', '" + I + "', or '" + D + "'.");
         }
         this.operation[i] = op;
         this.range[i] = range[j];
       }
-      this.length+=operation.length;
-    } else {
+      this.length += operation.length;
+    }
+    else
+    {
       this.operation = null;
       this.range = null;
-      this.length=0;
-      if (_setSeq(seq)) {
+      this.length = 0;
+      if (_setSeq(seq, false,0, 0))
+      {
         throw new Error("NOT YET Implemented: Constructing a Cigar object from a cigar string and a gapped sequence.");
       }
     }
   }
+
   /**
    * add range matched residues to cigar string
    * @param range int
    */
-  public void addMatch(int range) {
+  public void addMatch(int range)
+  {
     this.addOperation(M, range);
   }
+
   /**
    * Deleted regions mean that there will be discontinuous sequence numbering in the
    * sequence returned by getSeq(char).
-   * @return true if there are non-terminal deletions
+   * @return true if there deletions
    */
-  public boolean hasDeletedRegions() {
-    for (int i=1, l=length-1; i<l; i++) {
-      if (operation[i]==D)
+  public boolean hasDeletedRegions()
+  {
+    for (int i = 0, l = length; i < l; i++)
+    {
+      if (operation[i] == D)
+      {
         return true;
+      }
     }
     return false;
   }
-  protected static void addSequenceOps(CigarBase cigar, SequenceI seq, int startpos, int endpos) {
-  char op = '\0';
-  int range = 0;
-  int p = 0, res = seq.getLength();
-  startpos++;
-  endpos++;
-  while (p<res)
+
+  /**
+   * Adds
+   * insertion and match operations based on seq to the cigar up to
+   * the endpos column of seq.
+   *
+   * @param cigar CigarBase
+   * @param seq SequenceI
+   * @param startpos int
+   * @param endpos int
+   * @param initialDeletions if true then initial deletions will be added from start of seq to startpos
+   */
+  protected static void addSequenceOps(CigarBase cigar, SequenceI seq,
+                                       int startpos, int endpos, boolean initialDeletions)
   {
-    boolean isGap = jalview.util.Comparison.isGap(seq.getCharAt(p++));
-    if ( (startpos <= p) && (p < endpos))
+    char op = '\0';
+    int range = 0;
+    int p = 0, res = seq.getLength();
+
+    if (!initialDeletions)
+      p=startpos;
+
+
+    while (p <= endpos)
     {
-      if (isGap)
+      boolean isGap = (p < res) ? jalview.util.Comparison.isGap(seq.getCharAt(p)) : true;
+      if ( (startpos <= p) && (p <= endpos))
       {
-        if (range > 0 && op != I)
+        if (isGap)
+        {
+          if (range > 0 && op != I)
+          {
+            cigar.addOperation(op, range);
+            range = 0;
+          }
+          op = I;
+          range++;
+        }
+        else
         {
-          cigar.addOperation(op, range);
-          range = 0;
+          if (range > 0 && op != M)
+          {
+            cigar.addOperation(op, range);
+            range = 0;
+          }
+          op = M;
+          range++;
         }
-        op = I;
-        range++;
       }
       else
       {
-        if (range > 0 && op != M)
+        if (!isGap)
         {
-          cigar.addOperation(op, range);
-          range = 0;
+          if (range > 0 && op != D)
+          {
+            cigar.addOperation(op, range);
+            range = 0;
+          }
+          op = D;
+          range++;
         }
-        op = M;
-        range++;
-      }
-    }
-    else
-    {
-      if (!isGap)
-      {
-        if (range > 0 && op != D)
+        else
         {
-          cigar.addOperation(op, range);
-          range = 0;
+          // do nothing - insertions are not made in flanking regions
         }
-        op = D;
-        range++;
-      }
-      else
-      {
-        // do nothing - insertions are not recorded in flanking regions.
       }
+      p++;
+    }
+    if (range > 0)
+    {
+      cigar.addOperation(op, range);
     }
   }
-  if (range > 0)
-  {
-    cigar.addOperation(op, range);
-  }
-  }
+
   /**
    * create a cigar string for given sequence
    * @param seq SequenceI
    */
-  public SeqCigar(SequenceI seq) {
+  public SeqCigar(SequenceI seq)
+  {
     super();
     if (seq == null)
-      throw new Error("Implementation error for new Cigar(SequenceI)");
-    if (_setSeq(seq))
     {
-        // there is still work to do
-      addSequenceOps(this, seq, 0, seq.getLength());
+      throw new Error("Implementation error for new Cigar(SequenceI)");
     }
+    _setSeq(seq, false, 0, 0);
+    // there is still work to do
+    addSequenceOps(this, seq, 0, seq.getLength()-1, false);
   }
-  public SeqCigar(SequenceI seq, int start, int end) {
+
+  /**
+   * Create Cigar from a range of gaps and residues on a sequence object
+   * @param seq SequenceI
+   * @param start int - first column in range
+   * @param end int - last column in range
+   */
+  public SeqCigar(SequenceI seq, int start, int end)
+  {
     super();
     if (seq == null)
-      throw new Error("Implementation error for new Cigar(SequenceI)");
-    if (_setSeq(seq))
     {
-      // there is still work to do
-      addSequenceOps(this, seq, start, end);
+      throw new Error("Implementation error for new Cigar(SequenceI)");
     }
+    _setSeq(seq, false, start, end+1);
+    // there is still work to do
+    addSequenceOps(this, seq, start, end, false);
   }
 
   /**
@@ -233,6 +356,89 @@ public class SeqCigar
     Object[] opsandrange = parseCigarString(cigarString);
     return new SeqCigar(seq, (char[]) opsandrange[0], (int[]) opsandrange[1]);
   }
+
+  /**
+   * createAlignment
+   *
+   * @param alseqs SeqCigar[]
+   * @param gapCharacter char
+   * @return SequenceI[]
+   */
+  public static SequenceI[] createAlignmentSequences(SeqCigar[] alseqs,
+      char gapCharacter, ColumnSelection colsel)
+  {
+    SequenceI[] seqs = new SequenceI[alseqs.length];
+    Vector hiddenRegions=new Vector();
+    StringBuffer[] g_seqs = new StringBuffer[alseqs.length];
+    String[] alseqs_string=new String[alseqs.length];
+    Object[] gs_regions = new Object[alseqs.length];
+    for (int i = 0; i < alseqs.length; i++)
+    {
+      alseqs_string[i]=alseqs[i].getRefSeq().
+          getSequence().substring(alseqs[i].start,alseqs[i].end);
+      gs_regions[i] = alseqs[i].getSequenceAndDeletions(alseqs_string[i], gapCharacter); // gapped sequence, {start, start col, end. endcol}, hidden regions {{start, end, col}})
+      if (gs_regions[i] == null)
+      {
+        throw new Error("Implementation error: " + i +
+                        "'th sequence Cigar has no operations.");
+      }
+      g_seqs[i] = new StringBuffer( (String) ( (Object[]) gs_regions[i])[0]); // the visible gapped sequence
+    }
+    // Now account for insertions.
+    // this is complicated because we must keep track of shifted positions in each sequence
+    ShiftList shifts = new ShiftList();
+    for (int i = 0; i < alseqs.length; i++)
+    {
+      Object[] gs_region = ( (Object[]) ( (Object[]) gs_regions[i])[2]);
+      if (gs_region != null)
+
+      {
+        for (int hr = 0; hr < gs_region.length; hr++)
+        {
+          int[] region = (int[]) gs_region[hr];
+          char[] insert = new char[region[1] - region[0] + 1];
+          for (int s = 0; s < insert.length; s++)
+          {
+            insert[s] = gapCharacter;
+          }
+          int inspos = shifts.shift(region[2]); // resolve insertion position in current alignment frame of reference
+          for (int s = 0; s < alseqs.length; s++)
+          {
+            if (s != i)
+            {
+              if (g_seqs[s].length() <= inspos)
+              {
+                // prefix insertion with more gaps.
+                for (int l = inspos - g_seqs[s].length(); l > 0; l--)
+                {
+                  g_seqs[s].append(gapCharacter); // to debug - use a diffferent gap character here
+                }
+              }
+              g_seqs[s].insert(inspos, insert);
+            }
+            else
+            {
+              g_seqs[s].insert(inspos,
+                               alseqs_string[i].substring(region[0], region[1] + 1));
+            }
+          }
+          shifts.addShift(region[2], insert.length); // update shift in alignment frame of reference
+          colsel.hideColumns(inspos, inspos+insert.length-1);
+        }
+      }
+    }
+    for (int i = 0; i < alseqs.length; i++)
+    {
+      int[] bounds = ( (int[]) ( (Object[]) gs_regions[i])[1]);
+      SequenceI ref = alseqs[i].getRefSeq();
+      seqs[i] = new Sequence(ref.getName(), g_seqs[i].toString(),
+                             ref.getStart() + alseqs[i].start+bounds[0],
+                             ref.getStart() + alseqs[i].start+bounds[2]);
+      seqs[i].setDatasetSequence(ref);
+    }
+    return seqs;
+  }
+
   /**
    * non rigorous testing
    */
@@ -242,16 +448,25 @@ public class SeqCigar
    * @param ex_cs_gapped String
    * @return String
    */
-  public static String testCigar_string(Sequence seq, String ex_cs_gapped) {
+  public static String testCigar_string(Sequence seq, String ex_cs_gapped)
+  {
     SeqCigar c_sgapped = new SeqCigar(seq);
     String cs_gapped = c_sgapped.getCigarstring();
     if (!cs_gapped.equals(ex_cs_gapped))
-      System.err.println("Failed getCigarstring: incorect string '"+cs_gapped+"' != "+ex_cs_gapped);
+    {
+      System.err.println("Failed getCigarstring: incorect string '" + cs_gapped +
+                         "' != " + ex_cs_gapped);
+    }
     return cs_gapped;
   }
-  public static boolean testSeqRecovery(SeqCigar gen_sgapped, SequenceI s_gapped) {
+
+  public static boolean testSeqRecovery(SeqCigar gen_sgapped,
+                                        SequenceI s_gapped)
+  {
+      // this is non-rigorous - start and end  recovery is not tested.
     SequenceI gen_sgapped_s = gen_sgapped.getSeq('-');
-    if (!gen_sgapped_s.getSequence().equals(s_gapped.getSequence())) {
+    if (!gen_sgapped_s.getSequence().equals(s_gapped.getSequence()))
+    {
       System.err.println("Couldn't reconstruct sequence.\n" +
                          gen_sgapped_s.getSequence() + "\n" +
                          s_gapped.getSequence());
@@ -259,41 +474,119 @@ public class SeqCigar
     }
     return true;
   }
-  public static void main(String argv[]) throws Exception {
-    Sequence s=new Sequence("MySeq", "asdfktryasdtqwrtsaslldddptyipqqwaslchvhttt",39,80);
+
+  public static void main(String argv[])
+      throws Exception
+  {
+    String o_seq;
+    Sequence s = new Sequence("MySeq",
+                              o_seq = "asdfktryasdtqwrtsaslldddptyipqqwaslchvhttt",
+                              39, 80);
     String orig_gapped;
-    Sequence s_gapped=new Sequence("MySeq", orig_gapped="----asdf------ktryas---dtqwrtsasll----dddptyipqqwa----slchvhttt", 39,80);
-    String ex_cs_gapped="4I4M6I6M3I11M4I12M4I9M";
+    Sequence s_gapped = new Sequence("MySeq",
+        orig_gapped = "----asdf------ktryas---dtqwrtsasll----dddptyipqqwa----slchvhttt",
+                                     39, 80);
+    String ex_cs_gapped = "4I4M6I6M3I11M4I12M4I9M";
     s_gapped.setDatasetSequence(s);
     String sub_gapped_s;
-    Sequence s_subsequence_gapped=new Sequence("MySeq", sub_gapped_s="------ktryas---dtqwrtsasll----dddptyipqqwa----slchvh", 43,77);
+    Sequence s_subsequence_gapped = new Sequence("MySeq",
+        sub_gapped_s = "------ktryas---dtqwrtsasll----dddptyipqqwa----slchvh",
+                                                 43, 77);
 
     s_subsequence_gapped.setDatasetSequence(s);
     SeqCigar c_null = new SeqCigar(s);
     String cs_null = c_null.getCigarstring();
-    if (cs_null.length()>0)
-      System.err.println("Failed getCigarstring: Unexpected cigar operations:"+cs_null);
+    if (!cs_null.equals("42M"))
+    {
+      System.err.println(
+          "Failed to recover ungapped sequence cigar operations:" +
+          ( (cs_null == "") ? "empty string" : cs_null));
+    }
     testCigar_string(s_gapped, ex_cs_gapped);
     SeqCigar gen_sgapped = SeqCigar.parseCigar(s, ex_cs_gapped);
     if (!gen_sgapped.getCigarstring().equals(ex_cs_gapped))
-      System.err.println("Failed parseCigar("+ex_cs_gapped+")->getCigarString()->'"+gen_sgapped.getCigarstring()+"'");
+    {
+      System.err.println("Failed parseCigar(" + ex_cs_gapped +
+                         ")->getCigarString()->'" + gen_sgapped.getCigarstring() +
+                         "'");
+    }
     testSeqRecovery(gen_sgapped, s_gapped);
     // Test dataset resolution
     SeqCigar sub_gapped = new SeqCigar(s_subsequence_gapped);
     if (!testSeqRecovery(sub_gapped, s_subsequence_gapped))
-        System.err.println("Failed recovery for subsequence of dataset sequence");
+    {
+      System.err.println("Failed recovery for subsequence of dataset sequence");
+    }
     // width functions
-    if (sub_gapped.getWidth()!=sub_gapped_s.length())
+    if (sub_gapped.getWidth() != sub_gapped_s.length())
+    {
       System.err.println("Failed getWidth()");
+    }
 
     sub_gapped.getFullWidth();
     if (sub_gapped.hasDeletedRegions())
+    {
       System.err.println("hasDeletedRegions is incorrect.");
+    }
     // Test start-end region SeqCigar
-    SeqCigar sub_se_gp= new SeqCigar(s_subsequence_gapped, 8, 48);
-    if (sub_se_gp.getWidth()!=40)
-      System.err.println("SeqCigar(seq, start, end) not properly clipped alignsequence.");
-    System.out.println("Original sequence align:\n"+sub_gapped_s+"\nReconstructed window from 8 to 48\n"+"XXXXXXXX"+sub_se_gp.getSequenceString('-')+"...."+"\nCigar String:"+sub_se_gp.getCigarstring()+"");
+    SeqCigar sub_se_gp = new SeqCigar(s_subsequence_gapped, 8, 48);
+    if (sub_se_gp.getWidth() != 41)
+    {
+      System.err.println(
+          "SeqCigar(seq, start, end) not properly clipped alignsequence.");
+    }
+    System.out.println("Original sequence align:\n" + sub_gapped_s +
+                       "\nReconstructed window from 8 to 48\n"
+                       + "XXXXXXXX" + sub_se_gp.getSequenceString('-') + "..."
+                       + "\nCigar String:" + sub_se_gp.getCigarstring() + "\n"
+        );
+    SequenceI ssgp = sub_se_gp.getSeq('-');
+    System.out.println("\t " + ssgp.getSequence());
+    for (int r = 0; r < 10; r++)
+    {
+      sub_se_gp = new SeqCigar(s_subsequence_gapped, 8, 48);
+      int sl = sub_se_gp.getWidth();
+      int st = sl - r - r;
+      for (int rs = 0; rs < 10; rs++)
+      {
+        int e = st + rs;
+        sub_se_gp.deleteRange(st, e);
+        String ssgapedseq = sub_se_gp.getSeq('-').getSequence();
+        System.out.println(st + "," + e + "\t:" + ssgapedseq);
+      }
+    }
+    {
+      SeqCigar[] set = new SeqCigar[]
+          {
+          new SeqCigar(s), new SeqCigar(s_subsequence_gapped, 8, 48),
+          new SeqCigar(s_gapped)};
+      Alignment al = new Alignment(set);
+      for (int i = 0; i < al.getHeight(); i++)
+      {
+        System.out.println("" + al.getSequenceAt(i).getName() + "\t" +
+                           al.getSequenceAt(i).getStart() + "\t" +
+                           al.getSequenceAt(i).getEnd() + "\t" +
+                           al.getSequenceAt(i).getSequence());
+      }
+    }
+    {
+      System.out.println("Gapped.");
+      SeqCigar[] set = new SeqCigar[]
+          {
+          new SeqCigar(s), new SeqCigar(s_subsequence_gapped, 8, 48),
+          new SeqCigar(s_gapped)};
+      set[0].deleteRange(20, 25);
+      Alignment al = new Alignment(set);
+      for (int i = 0; i < al.getHeight(); i++)
+      {
+        System.out.println("" + al.getSequenceAt(i).getName() + "\t" +
+                           al.getSequenceAt(i).getStart() + "\t" +
+                           al.getSequenceAt(i).getEnd() + "\t" +
+                           al.getSequenceAt(i).getSequence());
+      }
+    }
+//    if (!ssgapedseq.equals("ryas---dtqqwa----slchvh"))
+//      System.err.println("Subseqgaped\n------ktryas---dtqwrtsasll----dddptyipqqwa----slchvhryas---dtqwrtsasll--qwa----slchvh\n"+ssgapedseq+"\n"+sub_se_gp.getCigarstring());
   }
 
 }
index 2127cd5..14bf680 100755 (executable)
@@ -60,16 +60,14 @@ public class AlignFrame
   Stack redoList = new Stack();\r
   private int treeCount = 0;\r
 \r
-\r
   /**\r
-   * Creates a new AlignFrame object.\r
-   *\r
-   * @param al DOCUMENT ME!\r
+   * new alignment window with hidden columns\r
+   * @param al AlignmentI\r
+   * @param hiddenColumns ColumnSelection or null\r
    */\r
-  public AlignFrame(AlignmentI al)\r
-  {\r
+  public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns) {\r
 \r
-    viewport = new AlignViewport(al);\r
+    viewport = new AlignViewport(al, hiddenColumns);\r
 \r
     this.setDropTarget(new java.awt.dnd.DropTarget(this, this));\r
 \r
@@ -141,6 +139,17 @@ public class AlignFrame
 \r
   }\r
 \r
+\r
+  /**\r
+   * Creates a new AlignFrame object.\r
+   *\r
+   * @param al DOCUMENT ME!\r
+   */\r
+  public AlignFrame(AlignmentI al)\r
+  {\r
+    this(al, null);\r
+  }\r
+\r
   public AlignViewport getViewport()\r
   {\r
     return viewport;\r
@@ -2289,27 +2298,28 @@ public class AlignFrame
    * or just the selected set will be submitted for multiple alignment.\r
    *\r
    */\r
-  private SequenceI[] gatherSequencesForAlignment()\r
+  private jalview.datamodel.AlignmentView gatherSequencesForAlignment()\r
   {\r
     // Now, check we have enough sequences\r
-    SequenceI[] msa = null;\r
+    AlignmentView msa = null;\r
 \r
     if ( (viewport.getSelectionGroup() != null) &&\r
         (viewport.getSelectionGroup().getSize(false) > 1))\r
     {\r
       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to some common interface!\r
-      SequenceGroup seqs = viewport.getSelectionGroup();\r
+      /*SequenceGroup seqs = viewport.getSelectionGroup();\r
       int sz;\r
       msa = new SequenceI[sz = seqs.getSize(false)];\r
 \r
       for (int i = 0; i < sz; i++)\r
       {\r
         msa[i] = (SequenceI) seqs.getSequenceAt(i);\r
-      }\r
+      } */\r
+      msa = viewport.getAlignmentView(true);\r
     }\r
     else\r
     {\r
-      Vector seqs = viewport.getAlignment().getSequences();\r
+      /*Vector seqs = viewport.getAlignment().getSequences();\r
 \r
       if (seqs.size() > 1)\r
       {\r
@@ -2319,7 +2329,8 @@ public class AlignFrame
         {\r
           msa[i] = (SequenceI) seqs.elementAt(i);\r
         }\r
-      }\r
+      }*/\r
+      msa = viewport.getAlignmentView(false);\r
     }\r
     return msa;\r
   }\r
@@ -2523,7 +2534,7 @@ public class AlignFrame
           {\r
             public void actionPerformed(ActionEvent e)\r
             {\r
-              SequenceI[] msa = gatherSequencesForAlignment();\r
+              AlignmentView msa = gatherSequencesForAlignment();\r
               new jalview.ws.MsaWSClient(sh, title, msa,\r
                   false, true, viewport.getAlignment().getDataset(), af);\r
 \r
@@ -2540,7 +2551,7 @@ public class AlignFrame
             {\r
               public void actionPerformed(ActionEvent e)\r
               {\r
-                SequenceI[] msa = gatherSequencesForAlignment();\r
+                AlignmentView msa = gatherSequencesForAlignment();\r
                 new jalview.ws.MsaWSClient(sh, title, msa,\r
                     true, true, viewport.getAlignment().getDataset(), af);\r
 \r
index f8be001..fde4d90 100755 (executable)
@@ -121,6 +121,20 @@ public class AlignViewport
         setAlignment(al);\r
         init();\r
     }\r
+    /**\r
+     * Create a new AlignViewport with hidden regions\r
+     * @param al AlignmentI\r
+     * @param hiddenColumns ColumnSelection\r
+     */\r
+    public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns) {\r
+      setAlignment(al);\r
+      if (hiddenColumns!=null) {\r
+        this.colSel = hiddenColumns;\r
+        if (hiddenColumns.getHiddenColumns() != null)\r
+          hasHiddenColumns = true;\r
+      }\r
+      init();\r
+    }\r
 \r
     void init()\r
     {\r
@@ -1188,13 +1202,13 @@ public class AlignViewport
         iSize = selectionGroup.getSize(false);\r
         seqs = selectionGroup.getSequencesInOrder(alignment);\r
         start = selectionGroup.getStartRes();\r
-        end = selectionGroup.getEndRes()+1;\r
+        end = selectionGroup.getEndRes(); // inclusive for start and end in SeqCigar constructor\r
       }\r
       else\r
       {\r
         iSize = alignment.getHeight();\r
         seqs = alignment.getSequencesArray();\r
-        end = alignment.getWidth();\r
+        end = alignment.getWidth()-1;\r
       }\r
       SeqCigar[] selseqs = new SeqCigar[iSize];\r
       for(i=0; i<iSize; i++)\r
@@ -1202,13 +1216,9 @@ public class AlignViewport
         selseqs[i] = new SeqCigar(seqs[i], start, end);\r
       }\r
       selection=new CigarArray(selseqs);\r
-\r
-      int[] hiddenregions = null;\r
-      char[] hr_ops = null;\r
+      // now construct the CigarArray operations\r
       if (hasHiddenColumns) {\r
-        Vector _hiddenregions = new Vector();\r
         Vector regions = colSel.getHiddenColumns();\r
-        int blockStart = start, blockEnd=end;\r
         int [] region;\r
         int hideStart, hideEnd;\r
         int last=start;\r
@@ -1250,7 +1260,22 @@ public class AlignViewport
       }\r
       return selection;\r
     }\r
-\r
+    /**\r
+     * return a compact representation of the current alignment selection to\r
+     * pass to an analysis function\r
+     * @param selectedOnly boolean true to just return the selected view\r
+     * @return AlignmentView\r
+     */\r
+    jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly) {\r
+      // JBPNote:\r
+      // this is here because the AlignmentView constructor modifies the CigarArray\r
+      // object. Refactoring of Cigar and alignment view representation should\r
+      // be done to remove redundancy.\r
+      CigarArray aligview = getViewAsCigars(selectedOnly);\r
+      if (aligview!=null)\r
+        return new AlignmentView(aligview);\r
+      return null;\r
+    }\r
     /**\r
      * This method returns the visible alignment as text, as\r
      * seen on the GUI, ie if columns are hidden they will not\r
index ffc5a76..5a22a26 100755 (executable)
@@ -44,7 +44,7 @@ public class PCAPanel extends GPCAPanel implements Runnable
     int top;\r
     RotatableCanvas rc;\r
     AlignViewport av;\r
-    String  [] seqstrings;\r
+    AlignmentView seqstrings;\r
     SequenceI  [] seqs;\r
 \r
     /**\r
@@ -59,9 +59,8 @@ public class PCAPanel extends GPCAPanel implements Runnable
 \r
         boolean sameLength = true;\r
 \r
-        seqstrings = av.getViewAsString(true);\r
-\r
-        if (av.getSelectionGroup() == null)\r
+        seqstrings = av.getAlignmentView(av.getSelectionGroup()!=null);\r
+        if(av.getSelectionGroup()==null)\r
         {\r
           seqs = av.alignment.getSequencesArray();\r
         }\r
@@ -69,12 +68,12 @@ public class PCAPanel extends GPCAPanel implements Runnable
         {\r
           seqs = av.getSelectionGroup().getSequencesInOrder(av.alignment);\r
         }\r
-\r
-        int length = seqs[0].getLength();\r
+        SeqCigar sq[]=seqstrings.getSequences();\r
+        int length = sq[0].getWidth();\r
 \r
         for (int i = 0; i < seqs.length; i++)\r
         {\r
-          if (seqs[i].getLength() != length)\r
+          if (sq[i].getWidth() != length)\r
           {\r
             sameLength = false;\r
             break;\r
@@ -123,7 +122,7 @@ public class PCAPanel extends GPCAPanel implements Runnable
     public void run()\r
     {\r
       try{\r
-        pca = new PCA(seqstrings);\r
+        pca = new PCA(seqstrings.getSequenceStrings(' '));\r
         pca.run();\r
 \r
         // Now find the component coordinates\r
@@ -257,18 +256,59 @@ public class PCAPanel extends GPCAPanel implements Runnable
 \r
     public void originalSeqData_actionPerformed(ActionEvent e)\r
     {\r
-      CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
-      for (int i = 0; i < seqs.length; i++)\r
+      // this was cut'n'pasted from the equivalent TreePanel method - we should make this an abstract function of all jalview analysis windows\r
+      if (seqstrings==null)\r
       {\r
-        cap.appendText(new jalview.util.Format("%-" + 15 + "s").form(\r
-            seqs[i].getName()));\r
-        cap.appendText(" " + seqstrings[i] + "\n");\r
-\r
+        jalview.bin.Cache.log.info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");\r
+        return;\r
       }\r
+      // decide if av alignment is sufficiently different to original data to warrant a new window to be created\r
+      // create new alignmnt window with hidden regions (unhiding hidden regions yields unaligned seqs)\r
+      // or create a selection box around columns in alignment view\r
+      // test Alignment(SeqCigar[])\r
+      Object[] alAndColsel = seqstrings.getAlignmentAndColumnSelection(av.\r
+          getGapCharacter());\r
+\r
+\r
+      if (alAndColsel != null && alAndColsel[0]!=null)\r
+      {\r
+          // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);\r
+\r
+          Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);\r
+          Alignment dataset = av.getAlignment().getDataset();\r
+          if (dataset != null)\r
+          {\r
+            al.setDataset(dataset);\r
+          }\r
+\r
+          if (true)\r
+          {\r
+              // make a new frame!\r
+              AlignFrame af = new AlignFrame(al, (ColumnSelection) alAndColsel[1]);\r
+\r
+              //>>>This is a fix for the moment, until a better solution is found!!<<<\r
+              // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());\r
+\r
+              //           af.addSortByOrderMenuItem(ServiceName + " Ordering",\r
+              //                                     msaorder);\r
+\r
+              Desktop.addInternalFrame(af, "original Data for " + this.title,\r
+                                       AlignFrame.NEW_WINDOW_WIDTH,\r
+                                       AlignFrame.NEW_WINDOW_HEIGHT);\r
+            }\r
+          }\r
+          /*      CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
+                 for (int i = 0; i < seqs.length; i++)\r
+                 {\r
+                   cap.appendText(new jalview.util.Format("%-" + 15 + "s").form(\r
+            seqs[i].getName()));\r
+                   cap.appendText(" " + seqstrings[i] + "\n");\r
 \r
-      Desktop.addInternalFrame(cap, "Original Data",\r
-          400, 400);\r
+                 }\r
 \r
+                 Desktop.addInternalFrame(cap, "Original Data",\r
+                     400, 400);\r
+           */\r
     }\r
 \r
 \r
index bde218c..d306028 100755 (executable)
@@ -137,7 +137,7 @@ public class TreePanel extends GTreePanel
     class TreeLoader extends Thread\r
     {\r
       NewickFile newtree;\r
-      jalview.datamodel.CigarArray odata=null;\r
+      jalview.datamodel.AlignmentView odata=null;\r
       public TreeLoader(NewickFile newtree)\r
       {\r
         this.newtree = newtree;\r
@@ -168,7 +168,7 @@ public class TreePanel extends GTreePanel
         {\r
           int start, end;\r
           SequenceI [] seqs;\r
-          CigarArray seqStrings = av.getViewAsCigars(av.getSelectionGroup()!=null);\r
+          AlignmentView seqStrings = av.getAlignmentView(av.getSelectionGroup()!=null);\r
           if(av.getSelectionGroup()==null)\r
           {\r
             start = 0;\r
@@ -316,16 +316,46 @@ public class TreePanel extends GTreePanel
 \r
     public void originalSeqData_actionPerformed(ActionEvent e)\r
     {\r
-      String originalData = tree.printOriginalSequenceData(av.getGapCharacter());\r
-      if (originalData!=null) {\r
-        CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
-        cap.setText(originalData);\r
-        Desktop.addInternalFrame(cap, "Original Data",\r
-                                 400, 400);\r
-      } else {\r
-        System.err.println("IMPLEMENTATION BUG! originalSeqData is not available.");\r
+      if (!tree.hasOriginalSequenceData())\r
+      {\r
+        jalview.bin.Cache.log.info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");\r
+        return;\r
       }\r
-\r
+      // decide if av alignment is sufficiently different to original data to warrant a new window to be created\r
+      // create new alignmnt window with hidden regions (unhiding hidden regions yields unaligned seqs)\r
+      // or create a selection box around columns in alignment view\r
+      // test Alignment(SeqCigar[])\r
+      Object[] alAndColsel = tree.seqData.getAlignmentAndColumnSelection(av.\r
+          getGapCharacter());\r
+\r
+\r
+      if (alAndColsel != null && alAndColsel[0]!=null)\r
+       {\r
+         // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);\r
+\r
+         Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);\r
+         Alignment dataset = av.getAlignment().getDataset();\r
+         if (dataset != null)\r
+         {\r
+           al.setDataset(dataset);\r
+         }\r
+\r
+         if (true)\r
+         {\r
+           // make a new frame!\r
+           AlignFrame af = new AlignFrame(al, (ColumnSelection) alAndColsel[1]);\r
+\r
+           //>>>This is a fix for the moment, until a better solution is found!!<<<\r
+           // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());\r
+\r
+       //           af.addSortByOrderMenuItem(ServiceName + " Ordering",\r
+       //                                     msaorder);\r
+\r
+           Desktop.addInternalFrame(af, "original Data for " + this.title,\r
+                                    AlignFrame.NEW_WINDOW_WIDTH,\r
+                                    AlignFrame.NEW_WINDOW_HEIGHT);\r
+         }\r
+       }\r
     }\r
 \r
 \r
diff --git a/src/jalview/util/ShiftList.java b/src/jalview/util/ShiftList.java
new file mode 100644 (file)
index 0000000..ca768ae
--- /dev/null
@@ -0,0 +1,74 @@
+package jalview.util;
+
+import java.util.*;
+
+/**
+ * ShiftList
+ * Simple way of mapping a linear series to a new linear range with new points introduced.
+ * Use at your own risk!
+ * <p>Title: ShiftList</p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2004</p>
+ *
+ * <p>Company: Dundee University</p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class ShiftList
+{
+    Vector shifts;
+  public ShiftList()
+  {
+    shifts = new Vector();
+  }
+
+  /**
+   * addShift
+   * @param pos start position for shift (in original reference frame)
+   * @param shift length of shift
+   */
+  public void addShift(int pos, int shift)
+  {
+    int sidx = 0;
+    int[] rshift=null;
+    while (sidx<shifts.size() && (rshift=(int[]) shifts.get(sidx))[0]<pos)
+      sidx++;
+    if (sidx==shifts.size())
+      shifts.insertElementAt(new int[] { pos, shift}, 0);
+    else
+      rshift[1]+=shift;
+  }
+
+  /**
+   * shift
+   *
+   * @param pos int
+   * @return int shifted position
+   */
+  public int shift(int pos)
+  {
+    if (shifts.size()==0)
+      return pos;
+    int shifted=pos;
+    int sidx=0;
+    int rshift[];
+    while (sidx<shifts.size()
+           &&
+           (rshift=((int[]) shifts.get(sidx++)))[0]<=pos) {
+      shifted += rshift[1];
+    }
+    return shifted;
+  }
+
+  /**
+   * clear all shifts
+   */
+  public void clear()
+  {
+    shifts.removeAllElements();
+  }
+
+}
index 999fc96..49c879b 100755 (executable)
@@ -58,7 +58,7 @@ public class MsaWSClient
    */\r
 \r
   public MsaWSClient(ext.vamsas.ServiceHandle sh, String altitle,\r
-                     SequenceI[] msa,\r
+                     jalview.datamodel.AlignmentView msa,\r
                      boolean submitGaps, boolean preserveOrder,\r
                      Alignment seqdataset,\r
                      AlignFrame _alignFrame)\r
@@ -91,7 +91,7 @@ public class MsaWSClient
   }\r
 \r
 \r
-  private void startMsaWSClient(String altitle, SequenceI[] msa,\r
+  private void startMsaWSClient(String altitle, AlignmentView msa,\r
                      boolean submitGaps, boolean preserveOrder, Alignment seqdataset)\r
   {\r
     if (!locateWebService())\r
@@ -141,6 +141,16 @@ public class MsaWSClient
     return true;\r
   }\r
 \r
+  protected String getServiceActionKey()\r
+  {\r
+    return "MsaWS";\r
+  }\r
+\r
+  protected String getServiceActionDescription()\r
+  {\r
+    return "Multiple Sequence Alignment";\r
+  }\r
+\r
   protected class MsaWSThread\r
       extends Thread implements WSClientI\r
   {\r
@@ -159,33 +169,34 @@ public class MsaWSClient
 \r
     Alignment dataset; // dataset to which the new alignment will be associated.\r
 \r
-    MsaWSThread(String title, SequenceI[] msa, boolean subgaps,\r
+    MsaWSThread(String title, AlignmentView _msa, boolean subgaps,\r
                 boolean presorder, Alignment seqset)\r
     {\r
+      // jbpnote - transformation should be above here - this is per sequence set contig, not for many contigs.\r
       alTitle = title;\r
       submitGaps = subgaps;\r
       preserveOrder = presorder;\r
       dataset = seqset;\r
       OutputHeader = wsInfo.getProgressText();\r
       SeqNames = new Hashtable();\r
-\r
+      SeqCigar[] msa = _msa.getSequences();\r
       vamsas.objects.simple.Sequence[] seqarray = new vamsas.objects.simple.\r
           Sequence[msa.length];\r
 \r
-      for (int i = 0; i < msa.length; i++)\r
+      for (int i = 0,n=0; i < msa.length; i++)\r
       {\r
         String newname = jalview.analysis.SeqsetUtils.unique_name(i);\r
-\r
+        SequenceI mseq = msa[i].getSeq('-');\r
         // uniquify as we go\r
         // TODO: JBPNote: this is a ubiquitous transformation - set of jalview seq objects to vamsas sequences with name preservation\r
         SeqNames.put(newname,\r
-                     jalview.analysis.SeqsetUtils.SeqCharacterHash(msa[i]));\r
+                     jalview.analysis.SeqsetUtils.SeqCharacterHash(mseq));\r
         seqarray[i] = new vamsas.objects.simple.Sequence();\r
         seqarray[i].setId(newname);\r
-        seqarray[i].setSeq( (submitGaps) ? msa[i].getSequence()\r
+        seqarray[i].setSeq( (submitGaps) ? mseq.getSequence()\r
                            : AlignSeq.extractGaps(\r
                                jalview.util.Comparison.GapChars,\r
-                               msa[i].getSequence()));\r
+                               mseq.getSequence()));\r
       }\r
 \r
       this.seqs = new vamsas.objects.simple.SequenceSet();\r