Merge branch 'bug/JAL-2541cutRelocateFeatures' into develop
[jalview.git] / src / jalview / commands / EditCommand.java
index d603a0d..1a227c5 100644 (file)
@@ -118,7 +118,7 @@ public class EditCommand implements CommandI
     public abstract Action getUndoAction();
   };
 
-  private List<Edit> edits = new ArrayList<Edit>();
+  private List<Edit> edits = new ArrayList<>();
 
   String description;
 
@@ -526,11 +526,6 @@ public class EditCommand implements CommandI
         command.string[i] = sequence.getSequence(command.position,
                 command.position + command.number);
         SequenceI oldds = sequence.getDatasetSequence();
-        if (command.oldds != null && command.oldds[i] != null)
-        {
-          // we are redoing an undone cut.
-          sequence.setDatasetSequence(null);
-        }
         Range cutPositions = sequence.findPositions(command.position + 1,
                 command.position + command.number);
         boolean cutIsInternal = cutPositions != null
@@ -544,27 +539,23 @@ public class EditCommand implements CommandI
         SequenceI ds = sequence.getDatasetSequence();
         sequence.deleteChars(command.position, command.position
                 + command.number);
-        SequenceI newDs = sequence.getDatasetSequence();
-        if (newDs != ds && command.al != null
-                && command.al.getDataset() != null
-                && !command.al.getDataset().getSequences().contains(newDs))
-        {
-          command.al.getDataset().addSequence(newDs);
-        }
 
         if (command.oldds != null && command.oldds[i] != null)
         {
-          // Undoing previous Paste - so
-          // oldds entry contains the cut dataset sequence,
-          // with sequence features in expected place.
+          /*
+           * we are Redoing a Cut, or Undoing a Paste - so
+           * oldds entry contains the cut dataset sequence,
+           * with sequence features in expected place
+           */
           sequence.setDatasetSequence(command.oldds[i]);
           command.oldds[i] = oldds;
         }
         else
         {
-          // New cut operation
-          // We always keep track of the dataset sequence so we can safely
-          // restore it during the Undo
+          /* 
+           * new cut operation: save the dataset sequence 
+           * so it can be restored in an Undo
+           */
           if (command.oldds == null)
           {
             command.oldds = new SequenceI[command.seqs.length];
@@ -584,6 +575,13 @@ public class EditCommand implements CommandI
             }
           }
         }
+        SequenceI newDs = sequence.getDatasetSequence();
+        if (newDs != ds && command.al != null
+                && command.al.getDataset() != null
+                && !command.al.getDataset().getSequences().contains(newDs))
+        {
+          command.al.getDataset().addSequence(newDs);
+        }
       }
 
       if (sequence.getLength() < 1)
@@ -720,6 +718,12 @@ public class EditCommand implements CommandI
           command.oldds[i] = sequence.getDatasetSequence();
           sameDatasetSequence = ds == sequence.getDatasetSequence();
           ds.setSequenceFeatures(sequence.getSequenceFeatures());
+          if (!sameDatasetSequence && command.al.getDataset() != null)
+          {
+            // delete 'undone' sequence from alignment dataset
+            command.al.getDataset()
+                    .deleteSequence(sequence.getDatasetSequence());
+          }
           sequence.setDatasetSequence(ds);
         }
         undoCutFeatures(command, command.seqs[i], start, length,
@@ -733,7 +737,7 @@ public class EditCommand implements CommandI
 
   static void replace(Edit command)
   {
-    StringBuffer tmp;
+    StringBuilder tmp;
     String oldstring;
     int start = command.position;
     int end = command.number;
@@ -762,20 +766,21 @@ public class EditCommand implements CommandI
 
       Range beforeEditedPositions = command.seqs[i].findPositions(1, start);
       Range afterEditedPositions = command.seqs[i]
-              .findPositions(start + end + 1, command.seqs[i].getLength());
+              .findPositions(end + 1, command.seqs[i].getLength());
       
       oldstring = command.seqs[i].getSequenceAsString();
-      tmp = new StringBuffer(oldstring.substring(0, start));
+      tmp = new StringBuilder(oldstring.substring(0, start));
       tmp.append(command.string[i]);
-      String nogaprep = jalview.analysis.AlignSeq.extractGaps(
-              jalview.util.Comparison.GapChars,
+      String nogaprep = AlignSeq.extractGaps(Comparison.GapChars,
               new String(command.string[i]));
-      int ipos = command.seqs[i].findPosition(start)
-              - command.seqs[i].getStart();
       if (end < oldstring.length())
       {
         tmp.append(oldstring.substring(end));
       }
+      // stash end prior to updating the sequence object so we can save it if
+      // need be.
+      Range oldstartend = new Range(command.seqs[i].getStart(),
+              command.seqs[i].getEnd());
       command.seqs[i].setSequence(tmp.toString());
       command.string[i] = oldstring
               .substring(start, Math.min(end, oldstring.length()))
@@ -785,31 +790,46 @@ public class EditCommand implements CommandI
 
       if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
       {
-        // probably need a new dataset sequence
-        if (newDSWasNeeded)
+        // we may already have dataset and limits stashed...
+        if (newDSWasNeeded || newStartEndWasNeeded)
         {
+          if (newDSWasNeeded)
+          {
           // then just switch the dataset sequence
           SequenceI oldds = command.seqs[i].getDatasetSequence();
           command.seqs[i].setDatasetSequence(command.oldds[i]);
           command.oldds[i] = oldds;
-        }
-        else
-        if (newStartEndWasNeeded)
-        {
-          Range newStart = command.oldStartEnd[i];
-          command.oldStartEnd[i] = new Range(command.seqs[i].getStart(),
-                  command.seqs[i].getEnd());
-          command.seqs[i].setStart(newStart.getBegin());
-          command.seqs[i].setEnd(newStart.getEnd());
+          }
+          if (newStartEndWasNeeded)
+          {
+            Range newStart = command.oldStartEnd[i];
+            command.oldStartEnd[i] = oldstartend;
+            command.seqs[i].setStart(newStart.getBegin());
+            command.seqs[i].setEnd(newStart.getEnd());
+          }
         }
         else         
         {
+          // decide if we need a new dataset sequence or modify start/end
           // first edit the original dataset sequence string
           SequenceI oldds = command.seqs[i].getDatasetSequence();
-          String fullseq, osp = oldds.getSequenceAsString();
-
-          fullseq = osp.substring(0, ipos) + nogaprep
-                  + osp.substring(ipos + nogaprep.length());
+          String osp = oldds.getSequenceAsString();
+          int beforeStartOfEdit = -oldds.getStart() + 1
+                  + (beforeEditedPositions == null
+                          ? ((afterEditedPositions != null)
+                                  ? afterEditedPositions.getBegin() - 1
+                                  : oldstartend.getBegin()
+                                          + nogapold.length())
+                          : beforeEditedPositions.getEnd()
+                  );
+          int afterEndOfEdit = -oldds.getStart() + 1
+                  + ((afterEditedPositions == null)
+                  ? oldstartend.getEnd()
+                          : afterEditedPositions.getBegin() - 1);
+          String fullseq = osp.substring(0,
+                  beforeStartOfEdit)
+                  + nogaprep
+                  + osp.substring(afterEndOfEdit);
 
           // and check if new sequence data is different..
           if (!fullseq.equalsIgnoreCase(osp))
@@ -817,13 +837,21 @@ public class EditCommand implements CommandI
             // old ds and edited ds are different, so
             // create the new dataset sequence
             SequenceI newds = new Sequence(oldds);
-            newds.setSequence(fullseq.toUpperCase());
+            newds.setSequence(fullseq);
 
             if (command.oldds == null)
             {
               command.oldds = new SequenceI[command.seqs.length];
             }
             command.oldds[i] = command.seqs[i].getDatasetSequence();
+
+            // And preserve start/end for good-measure
+
+            if (command.oldStartEnd == null)
+            {
+              command.oldStartEnd = new Range[command.seqs.length];
+            }
+            command.oldStartEnd[i] = oldstartend;
             // TODO: JAL-1131 ensure newly created dataset sequence is added to
             // the set of
             // dataset sequences associated with the alignment.
@@ -846,7 +874,8 @@ public class EditCommand implements CommandI
             {
               // modification at end
               command.seqs[i].setEnd(
-                      beforeEditedPositions.getEnd() + nogaprep.length());
+                      beforeEditedPositions.getEnd() + nogaprep.length()
+                              - nogapold.length());
             }
             else if (afterEditedPositions != null
                     && beforeEditedPositions == null)
@@ -860,8 +889,7 @@ public class EditCommand implements CommandI
               // edit covered both start and end. Here we can only guess the
               // new
               // start/end
-              String nogapalseq = jalview.analysis.AlignSeq.extractGaps(
-                      jalview.util.Comparison.GapChars,
+              String nogapalseq = AlignSeq.extractGaps(Comparison.GapChars,
                       command.seqs[i].getSequenceAsString().toUpperCase());
               int newStart = command.seqs[i].getDatasetSequence()
                       .getSequenceAsString().indexOf(nogapalseq);
@@ -891,7 +919,7 @@ public class EditCommand implements CommandI
     if (modifyVisibility && !insert)
     {
       // only occurs if a sequence was added or deleted.
-      command.deletedAnnotationRows = new Hashtable<SequenceI, AlignmentAnnotation[]>();
+      command.deletedAnnotationRows = new Hashtable<>();
     }
     if (command.fullAlignmentHeight)
     {
@@ -1049,7 +1077,7 @@ public class EditCommand implements CommandI
 
     if (!insert)
     {
-      command.deletedAnnotations = new Hashtable<String, Annotation[]>();
+      command.deletedAnnotations = new Hashtable<>();
     }
 
     int aSize;
@@ -1333,7 +1361,7 @@ public class EditCommand implements CommandI
    */
   public Map<SequenceI, SequenceI> priorState(boolean forUndo)
   {
-    Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
+    Map<SequenceI, SequenceI> result = new HashMap<>();
     if (getEdits() == null)
     {
       return result;
@@ -1366,7 +1394,7 @@ public class EditCommand implements CommandI
      * Work backwards through the edit list, deriving the sequences before each
      * was applied. The final result is the sequence set before any edits.
      */
-    Iterator<Edit> editList = new ReverseListIterator<Edit>(getEdits());
+    Iterator<Edit> editList = new ReverseListIterator<>(getEdits());
     while (editList.hasNext())
     {
       Edit oldEdit = editList.next();
@@ -1415,43 +1443,45 @@ public class EditCommand implements CommandI
 
   public class Edit
   {
-    public SequenceI[] oldds;
+    private SequenceI[] oldds;
 
     /**
      * start and end of sequence prior to edit
      */
-    public Range[] oldStartEnd;
+    private Range[] oldStartEnd;
 
-    boolean fullAlignmentHeight = false;
+    private boolean fullAlignmentHeight = false;
 
-    Map<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
+    private Map<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
 
-    Map<String, Annotation[]> deletedAnnotations;
+    private Map<String, Annotation[]> deletedAnnotations;
 
     /*
      * features deleted by the cut (re-add on Undo)
      * (including the original of any shortened features)
      */
-    Map<SequenceI, List<SequenceFeature>> deletedFeatures;
+    private Map<SequenceI, List<SequenceFeature>> deletedFeatures;
 
     /*
      * shortened features added by the cut (delete on Undo)
      */
-    Map<SequenceI, List<SequenceFeature>> truncatedFeatures;
+    private Map<SequenceI, List<SequenceFeature>> truncatedFeatures;
 
-    AlignmentI al;
+    private AlignmentI al;
 
-    Action command;
+    final private Action command;
 
     char[][] string;
 
     SequenceI[] seqs;
 
-    int[] alIndex;
+    private int[] alIndex;
+
+    private int position;
 
-    int position, number;
+    private int number;
 
-    char gapChar;
+    private char gapChar;
 
     public Edit(Action cmd, SequenceI[] sqs, int pos, int count,
             char gap)
@@ -1479,6 +1509,16 @@ public class EditCommand implements CommandI
       fullAlignmentHeight = (align.getHeight() == sqs.length);
     }
 
+    /**
+     * Constructor given a REPLACE command and the replacement string
+     * 
+     * @param cmd
+     * @param sqs
+     * @param pos
+     * @param count
+     * @param align
+     * @param replace
+     */
     Edit(Action cmd, SequenceI[] sqs, int pos, int count,
             AlignmentI align, String replace)
     {
@@ -1532,7 +1572,7 @@ public class EditCommand implements CommandI
     }
     else
     {
-      return new ReverseListIterator<Edit>(getEdits());
+      return new ReverseListIterator<>(getEdits());
     }
   }