JAL-353 updated patch for undo successive remove redundant sequence
[jalview.git] / src / jalview / commands / EditCommand.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.commands;
22
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.Annotation;
26 import jalview.datamodel.Sequence;
27 import jalview.datamodel.SequenceFeature;
28 import jalview.datamodel.SequenceI;
29
30 import java.util.Hashtable;
31 import java.util.List;
32
33 /**
34  * 
35  * <p>
36  * Title: EditCommmand
37  * </p>
38  * 
39  * <p>
40  * Description: Essential information for performing undo and redo for cut/paste
41  * insert/delete gap which can be stored in the HistoryList
42  * </p>
43  * 
44  * <p>
45  * Copyright: Copyright (c) 2006
46  * </p>
47  * 
48  * <p>
49  * Company: Dundee University
50  * </p>
51  * 
52  * @author not attributable
53  * @version 1.0
54  */
55 public class EditCommand implements CommandI
56 {
57   public static final int INSERT_GAP = 0;
58
59   public static final int DELETE_GAP = 1;
60
61   public static final int CUT = 2;
62
63   public static final int PASTE = 3;
64
65   public static final int REPLACE = 4;
66
67   public static final int INSERT_NUC = 5;
68
69   Edit[] edits;
70
71   String description;
72
73   public EditCommand()
74   {
75   }
76
77   public EditCommand(String description)
78   {
79     this.description = description;
80   }
81
82   public EditCommand(String description, int command, SequenceI[] seqs,
83           int position, int number, AlignmentI al)
84   {
85     this.description = description;
86     if (command == CUT || command == PASTE)
87     {
88       edits = new Edit[]
89       { new Edit(command, seqs, position, number, al) };
90     }
91
92     performEdit(0, null);
93   }
94
95   public EditCommand(String description, int command, String replace,
96           SequenceI[] seqs, int position, int number, AlignmentI al)
97   {
98     this.description = description;
99     if (command == REPLACE)
100     {
101       edits = new Edit[]
102       { new Edit(command, seqs, position, number, al, replace) };
103     }
104
105     performEdit(0, null);
106   }
107
108   @Override
109   final public String getDescription()
110   {
111     return description;
112   }
113
114   @Override
115   public int getSize()
116   {
117     return edits == null ? 0 : edits.length;
118   }
119
120   final public AlignmentI getAlignment()
121   {
122     return edits[0].al;
123   }
124
125   /**
126    * append a new editCommand Note. this shouldn't be called if the edit is an
127    * operation affects more alignment objects than the one referenced in al (for
128    * example, cut or pasting whole sequences). Use the form with an additional
129    * AlignmentI[] views parameter.
130    * 
131    * @param command
132    * @param seqs
133    * @param position
134    * @param number
135    * @param al
136    * @param performEdit
137    */
138   final public void appendEdit(int command, SequenceI[] seqs, int position,
139           int number, AlignmentI al, boolean performEdit)
140   {
141     appendEdit(command, seqs, position, number, al, performEdit, null);
142   }
143
144   /**
145    * append a new edit command with a set of alignment views that may be
146    * operated on
147    * 
148    * @param command
149    * @param seqs
150    * @param position
151    * @param number
152    * @param al
153    * @param performEdit
154    * @param views
155    */
156   final public void appendEdit(int command, SequenceI[] seqs, int position,
157           int number, AlignmentI al, boolean performEdit, AlignmentI[] views)
158   {
159     Edit edit = new Edit(command, seqs, position, number,
160             al.getGapCharacter());
161     if (al.getHeight() == seqs.length)
162     {
163       edit.al = al;
164       edit.fullAlignmentHeight = true;
165     }
166
167     if (edits != null)
168     {
169       Edit[] temp = new Edit[edits.length + 1];
170       System.arraycopy(edits, 0, temp, 0, edits.length);
171       edits = temp;
172       edits[edits.length - 1] = edit;
173     }
174     else
175     {
176       edits = new Edit[]
177       { edit };
178     }
179
180     if (performEdit)
181     {
182       performEdit(edits.length - 1, views);
183     }
184   }
185
186   final void performEdit(int commandIndex, AlignmentI[] views)
187   {
188     int eSize = edits.length;
189     for (int e = commandIndex; e < eSize; e++)
190     {
191       switch (edits[e].command)
192       {
193       case INSERT_GAP:
194         insertGap(edits[e]);
195         break;
196       case DELETE_GAP:
197         deleteGap(edits[e]);
198         break;
199       case CUT:
200         cut(edits[e], views);
201         break;
202       case PASTE:
203         paste(edits[e], views);
204         break;
205       case REPLACE:
206         replace(edits[e]);
207         break;
208       // TODO:add deleteNuc for UNDO
209       // case INSERT_NUC:
210       // insertNuc(edits[e]);
211       // break;
212       }
213     }
214   }
215
216   @Override
217   final public void doCommand(AlignmentI[] views)
218   {
219     performEdit(0, views);
220   }
221
222   @Override
223   final public void undoCommand(AlignmentI[] views)
224   { 
225     for(Edit e : edits){
226         switch (e.command)
227         {
228         case INSERT_GAP:
229           deleteGap(e);
230           break;
231         case DELETE_GAP:
232           insertGap(e);
233           break;
234         case CUT:
235           paste(e, views);
236           break;
237         case PASTE:
238           cut(e, views);
239           break;
240         case REPLACE:
241           replace(e);
242           break;
243         }
244     }
245   }
246
247   final void insertGap(Edit command)
248   {
249
250     for (int s = 0; s < command.seqs.length; s++)
251     {
252       command.seqs[s].insertCharAt(command.position, command.number,
253               command.gapChar);
254       // System.out.println("pos: "+command.position+" number: "+command.number);
255     }
256
257     adjustAnnotations(command, true, false, null);
258   }
259
260   //
261   // final void insertNuc(Edit command)
262   // {
263   //
264   // for (int s = 0; s < command.seqs.length; s++)
265   // {
266   // System.out.println("pos: "+command.position+" number: "+command.number);
267   // command.seqs[s].insertCharAt(command.position, command.number,'A');
268   // }
269   //
270   // adjustAnnotations(command, true, false, null);
271   // }
272
273   final void deleteGap(Edit command)
274   {
275     for (int s = 0; s < command.seqs.length; s++)
276     {
277       command.seqs[s].deleteChars(command.position, command.position
278               + command.number);
279     }
280
281     adjustAnnotations(command, false, false, null);
282   }
283
284   void cut(Edit command, AlignmentI[] views)
285   {
286     boolean seqDeleted = false;
287     command.string = new char[command.seqs.length][];
288
289     for (int i = 0; i < command.seqs.length; i++)
290     {
291       if (command.seqs[i].getLength() > command.position)
292       {
293         command.string[i] = command.seqs[i].getSequence(command.position,
294                 command.position + command.number);
295         SequenceI oldds = command.seqs[i].getDatasetSequence();
296         if (command.oldds != null && command.oldds[i] != null)
297         {
298           // we are redoing an undone cut.
299           command.seqs[i].setDatasetSequence(null);
300         }
301         command.seqs[i].deleteChars(command.position, command.position
302                 + command.number);
303         if (command.oldds != null && command.oldds[i] != null)
304         {
305           // oldds entry contains the cut dataset sequence.
306           command.seqs[i].setDatasetSequence(command.oldds[i]);
307           command.oldds[i] = oldds;
308         }
309         else
310         {
311           // modify the oldds if necessary
312           if (oldds != command.seqs[i].getDatasetSequence()
313                   || command.seqs[i].getSequenceFeatures() != null)
314           {
315             if (command.oldds == null)
316             {
317               command.oldds = new SequenceI[command.seqs.length];
318             }
319             command.oldds[i] = oldds;
320             adjustFeatures(
321                     command,
322                     i,
323                     command.seqs[i].findPosition(command.position),
324                     command.seqs[i].findPosition(command.position
325                             + command.number), false);
326           }
327         }
328       }
329
330       if (command.seqs[i].getLength() < 1)
331       {
332         command.al.deleteSequence(command.seqs[i]);
333         seqDeleted = true;
334       }
335     }
336
337     adjustAnnotations(command, false, seqDeleted, views);
338   }
339
340   void paste(Edit command, AlignmentI[] views)
341   {
342     StringBuffer tmp;
343     boolean newDSNeeded;
344     boolean newDSWasNeeded;
345     int newstart, newend;
346     boolean seqWasDeleted = false;
347     int start = 0, end = 0;
348
349     for (int i = 0; i < command.seqs.length; i++)
350     {
351       newDSNeeded = false;
352       newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
353       if (command.seqs[i].getLength() < 1)
354       {
355         // ie this sequence was deleted, we need to
356         // read it to the alignment
357         if (command.alIndex[i] < command.al.getHeight())
358         {
359           List<SequenceI> sequences;
360           synchronized (sequences = command.al.getSequences())
361           {
362             if (!(command.alIndex[i] < 0))
363             {
364               sequences.add(command.alIndex[i], command.seqs[i]);
365             }
366           }
367         }
368         else
369         {
370           command.al.addSequence(command.seqs[i]);
371         }
372         seqWasDeleted = true;
373       }
374       newstart = command.seqs[i].getStart();
375       newend = command.seqs[i].getEnd();
376
377       tmp = new StringBuffer();
378       tmp.append(command.seqs[i].getSequence());
379       // Undo of a delete does not replace original dataset sequence on to
380       // alignment sequence.
381
382       if (command.string != null && command.string[i] != null)
383       {
384         if (command.position >= tmp.length())
385         {
386           // This occurs if padding is on, and residues
387           // are removed from end of alignment
388           int length = command.position - tmp.length();
389           while (length > 0)
390           {
391             tmp.append(command.gapChar);
392             length--;
393           }
394         }
395         tmp.insert(command.position, command.string[i]);
396         for (int s = 0; s < command.string[i].length; s++)
397         {
398           if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
399           {
400             if (!newDSNeeded)
401             {
402               newDSNeeded = true;
403               start = command.seqs[i].findPosition(command.position);
404               end = command.seqs[i].findPosition(command.position
405                       + command.number);
406             }
407             if (command.seqs[i].getStart() == start)
408             {
409               newstart--;
410             }
411             else
412             {
413               newend++;
414             }
415           }
416         }
417         command.string[i] = null;
418       }
419
420       command.seqs[i].setSequence(tmp.toString());
421       command.seqs[i].setStart(newstart);
422       command.seqs[i].setEnd(newend);
423       if (newDSNeeded)
424       {
425         if (command.seqs[i].getDatasetSequence() != null)
426         {
427           SequenceI ds;
428           if (newDSWasNeeded)
429           {
430             ds = command.oldds[i];
431           }
432           else
433           {
434             // make a new DS sequence
435             // use new ds mechanism here
436             ds = new Sequence(command.seqs[i].getName(),
437                     jalview.analysis.AlignSeq.extractGaps(
438                             jalview.util.Comparison.GapChars,
439                             command.seqs[i].getSequenceAsString()),
440                     command.seqs[i].getStart(), command.seqs[i].getEnd());
441             ds.setDescription(command.seqs[i].getDescription());
442           }
443           if (command.oldds == null)
444           {
445             command.oldds = new SequenceI[command.seqs.length];
446           }
447           command.oldds[i] = command.seqs[i].getDatasetSequence();
448           command.seqs[i].setDatasetSequence(ds);
449         }
450         adjustFeatures(command, i, start, end, true);
451       }
452     }
453     adjustAnnotations(command, true, seqWasDeleted, views);
454
455     command.string = null;
456   }
457
458   void replace(Edit command)
459   {
460     StringBuffer tmp;
461     String oldstring;
462     int start = command.position;
463     int end = command.number;
464     // TODO TUTORIAL - Fix for replacement with different length of sequence (or
465     // whole sequence)
466     // TODO Jalview 2.4 bugfix change to an aggregate command - original
467     // sequence string is cut, new string is pasted in.
468     command.number = start + command.string[0].length;
469     for (int i = 0; i < command.seqs.length; i++)
470     {
471       boolean newDSWasNeeded = command.oldds != null
472               && command.oldds[i] != null;
473
474       /**
475        * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
476        * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
477        * viewport.alignment));
478        * 
479        */
480       /**
481        * then addHistoryItem(new EditCommand( "Add sequences",
482        * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
483        * 
484        */
485       oldstring = command.seqs[i].getSequenceAsString();
486       tmp = new StringBuffer(oldstring.substring(0, start));
487       tmp.append(command.string[i]);
488       String nogaprep = jalview.analysis.AlignSeq.extractGaps(
489               jalview.util.Comparison.GapChars, new String(
490                       command.string[i]));
491       int ipos = command.seqs[i].findPosition(start)
492               - command.seqs[i].getStart();
493       tmp.append(oldstring.substring(end));
494       command.seqs[i].setSequence(tmp.toString());
495       command.string[i] = oldstring.substring(start, end).toCharArray();
496       String nogapold = jalview.analysis.AlignSeq.extractGaps(
497               jalview.util.Comparison.GapChars, new String(
498                       command.string[i]));
499       if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
500       {
501         if (newDSWasNeeded)
502         {
503           SequenceI oldds = command.seqs[i].getDatasetSequence();
504           command.seqs[i].setDatasetSequence(command.oldds[i]);
505           command.oldds[i] = oldds;
506         }
507         else
508         {
509           if (command.oldds == null)
510           {
511             command.oldds = new SequenceI[command.seqs.length];
512           }
513           command.oldds[i] = command.seqs[i].getDatasetSequence();
514           SequenceI newds = new Sequence(
515                   command.seqs[i].getDatasetSequence());
516           String fullseq, osp = newds.getSequenceAsString();
517           fullseq = osp.substring(0, ipos) + nogaprep
518                   + osp.substring(ipos + nogaprep.length());
519           newds.setSequence(fullseq.toUpperCase());
520           // TODO: JAL-1131 ensure newly created dataset sequence is added to
521           // the set of
522           // dataset sequences associated with the alignment.
523           // TODO: JAL-1131 fix up any annotation associated with new dataset
524           // sequence to ensure that original sequence/annotation relationships
525           // are preserved.
526           command.seqs[i].setDatasetSequence(newds);
527
528         }
529       }
530       tmp = null;
531       oldstring = null;
532     }
533   }
534
535   final void adjustAnnotations(Edit command, boolean insert,
536           boolean modifyVisibility, AlignmentI[] views)
537   {
538     AlignmentAnnotation[] annotations = null;
539
540     if (modifyVisibility && !insert)
541     {
542       // only occurs if a sequence was added or deleted.
543       command.deletedAnnotationRows = new Hashtable();
544     }
545     if (command.fullAlignmentHeight)
546     {
547       annotations = command.al.getAlignmentAnnotation();
548     }
549     else
550     {
551       int aSize = 0;
552       AlignmentAnnotation[] tmp;
553       for (int s = 0; s < command.seqs.length; s++)
554       {
555         if (modifyVisibility)
556         {
557           // Rows are only removed or added to sequence object.
558           if (!insert)
559           {
560             // remove rows
561             tmp = command.seqs[s].getAnnotation();
562             if (tmp != null)
563             {
564               int alen = tmp.length;
565               for (int aa = 0; aa < tmp.length; aa++)
566               {
567                 if (!command.al.deleteAnnotation(tmp[aa]))
568                 {
569                   // strip out annotation not in the current al (will be put
570                   // back on insert in all views)
571                   tmp[aa] = null;
572                   alen--;
573                 }
574               }
575               command.seqs[s].setAlignmentAnnotation(null);
576               if (alen != tmp.length)
577               {
578                 // save the non-null annotation references only
579                 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
580                 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
581                 {
582                   if (tmp[aa] != null)
583                   {
584                     saved[aapos++] = tmp[aa];
585                     tmp[aa] = null;
586                   }
587                 }
588                 tmp = saved;
589                 command.deletedAnnotationRows.put(command.seqs[s], saved);
590                 // and then remove any annotation in the other views
591                 for (int alview = 0; views != null && alview < views.length; alview++)
592                 {
593                   if (views[alview] != command.al)
594                   {
595                     AlignmentAnnotation[] toremove = views[alview]
596                             .getAlignmentAnnotation();
597                     if (toremove == null || toremove.length == 0)
598                     {
599                       continue;
600                     }
601                     // remove any alignment annotation on this sequence that's
602                     // on that alignment view.
603                     for (int aa = 0; aa < toremove.length; aa++)
604                     {
605                       if (toremove[aa].sequenceRef == command.seqs[s])
606                       {
607                         views[alview].deleteAnnotation(toremove[aa]);
608                       }
609                     }
610                   }
611                 }
612               }
613               else
614               {
615                 // save all the annotation
616                 command.deletedAnnotationRows.put(command.seqs[s], tmp);
617               }
618             }
619           }
620           else
621           {
622             // recover rows
623             if (command.deletedAnnotationRows != null
624                     && command.deletedAnnotationRows
625                             .containsKey(command.seqs[s]))
626             {
627               AlignmentAnnotation[] revealed = (AlignmentAnnotation[]) command.deletedAnnotationRows
628                       .get(command.seqs[s]);
629               command.seqs[s].setAlignmentAnnotation(revealed);
630               if (revealed != null)
631               {
632                 for (int aa = 0; aa < revealed.length; aa++)
633                 {
634                   // iterate through al adding original annotation
635                   command.al.addAnnotation(revealed[aa]);
636                 }
637                 for (int aa = 0; aa < revealed.length; aa++)
638                 {
639                   command.al.setAnnotationIndex(revealed[aa], aa);
640                 }
641                 // and then duplicate added annotation on every other alignment
642                 // view
643                 for (int vnum = 0; views != null && vnum < views.length; vnum++)
644                 {
645                   if (views[vnum] != command.al)
646                   {
647                     int avwidth = views[vnum].getWidth() + 1;
648                     // duplicate in this view
649                     for (int a = 0; a < revealed.length; a++)
650                     {
651                       AlignmentAnnotation newann = new AlignmentAnnotation(
652                               revealed[a]);
653                       command.seqs[s].addAlignmentAnnotation(newann);
654                       newann.padAnnotation(avwidth);
655                       views[vnum].addAnnotation(newann);
656                       views[vnum].setAnnotationIndex(newann, a);
657                     }
658                   }
659                 }
660               }
661             }
662           }
663           continue;
664         }
665
666         if (command.seqs[s].getAnnotation() == null)
667         {
668           continue;
669         }
670
671         if (aSize == 0)
672         {
673           annotations = command.seqs[s].getAnnotation();
674         }
675         else
676         {
677           tmp = new AlignmentAnnotation[aSize
678                   + command.seqs[s].getAnnotation().length];
679
680           System.arraycopy(annotations, 0, tmp, 0, aSize);
681
682           System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
683                   command.seqs[s].getAnnotation().length);
684
685           annotations = tmp;
686         }
687         aSize = annotations.length;
688       }
689     }
690
691     if (annotations == null)
692     {
693       return;
694     }
695
696     if (!insert)
697     {
698       command.deletedAnnotations = new Hashtable();
699     }
700
701     int aSize;
702     Annotation[] temp;
703     for (int a = 0; a < annotations.length; a++)
704     {
705       if (annotations[a].autoCalculated
706               || annotations[a].annotations == null)
707       {
708         continue;
709       }
710
711       int tSize = 0;
712
713       aSize = annotations[a].annotations.length;
714       if (insert)
715       {
716         temp = new Annotation[aSize + command.number];
717         if (annotations[a].padGaps)
718         {
719           for (int aa = 0; aa < temp.length; aa++)
720           {
721             temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
722           }
723         }
724       }
725       else
726       {
727         if (command.position < aSize)
728         {
729           if (command.position + command.number >= aSize)
730           {
731             tSize = aSize;
732           }
733           else
734           {
735             tSize = aSize - command.number;
736           }
737         }
738         else
739         {
740           tSize = aSize;
741         }
742
743         if (tSize < 0)
744         {
745           tSize = aSize;
746         }
747         temp = new Annotation[tSize];
748       }
749
750       if (insert)
751       {
752         if (command.position < annotations[a].annotations.length)
753         {
754           System.arraycopy(annotations[a].annotations, 0, temp, 0,
755                   command.position);
756
757           if (command.deletedAnnotations != null
758                   && command.deletedAnnotations
759                           .containsKey(annotations[a].annotationId))
760           {
761             Annotation[] restore = (Annotation[]) command.deletedAnnotations
762                     .get(annotations[a].annotationId);
763
764             System.arraycopy(restore, 0, temp, command.position,
765                     command.number);
766
767           }
768
769           System.arraycopy(annotations[a].annotations, command.position,
770                   temp, command.position + command.number, aSize
771                           - command.position);
772         }
773         else
774         {
775           if (command.deletedAnnotations != null
776                   && command.deletedAnnotations
777                           .containsKey(annotations[a].annotationId))
778           {
779             Annotation[] restore = (Annotation[]) command.deletedAnnotations
780                     .get(annotations[a].annotationId);
781
782             temp = new Annotation[annotations[a].annotations.length
783                     + restore.length];
784             System.arraycopy(annotations[a].annotations, 0, temp, 0,
785                     annotations[a].annotations.length);
786             System.arraycopy(restore, 0, temp,
787                     annotations[a].annotations.length, restore.length);
788           }
789           else
790           {
791             temp = annotations[a].annotations;
792           }
793         }
794       }
795       else
796       {
797         if (tSize != aSize || command.position < 2)
798         {
799           int copylen = Math.min(command.position,
800                   annotations[a].annotations.length);
801           if (copylen > 0)
802            {
803             System.arraycopy(annotations[a].annotations, 0, temp, 0,
804                     copylen); // command.position);
805           }
806
807           Annotation[] deleted = new Annotation[command.number];
808           if (copylen >= command.position)
809           {
810             copylen = Math.min(command.number,
811                     annotations[a].annotations.length - command.position);
812             if (copylen > 0)
813             {
814               System.arraycopy(annotations[a].annotations,
815                       command.position, deleted, 0, copylen); // command.number);
816             }
817           }
818
819           command.deletedAnnotations.put(annotations[a].annotationId,
820                   deleted);
821           if (annotations[a].annotations.length > command.position
822                   + command.number)
823           {
824             System.arraycopy(annotations[a].annotations, command.position
825                     + command.number, temp, command.position,
826                     annotations[a].annotations.length - command.position
827                             - command.number); // aSize
828           }
829         }
830         else
831         {
832           int dSize = aSize - command.position;
833
834           if (dSize > 0)
835           {
836             Annotation[] deleted = new Annotation[command.number];
837             System.arraycopy(annotations[a].annotations, command.position,
838                     deleted, 0, dSize);
839
840             command.deletedAnnotations.put(annotations[a].annotationId,
841                     deleted);
842
843             tSize = Math.min(annotations[a].annotations.length,
844                     command.position);
845             temp = new Annotation[tSize];
846             System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
847           }
848           else
849           {
850             temp = annotations[a].annotations;
851           }
852         }
853       }
854
855       annotations[a].annotations = temp;
856     }
857   }
858
859   final void adjustFeatures(Edit command, int index, int i, int j,
860           boolean insert)
861   {
862     SequenceI seq = command.seqs[index];
863     SequenceI sequence = seq.getDatasetSequence();
864     if (sequence == null)
865     {
866       sequence = seq;
867     }
868
869     if (insert)
870     {
871       if (command.editedFeatures != null
872               && command.editedFeatures.containsKey(seq))
873       {
874         sequence.setSequenceFeatures((SequenceFeature[]) command.editedFeatures
875                 .get(seq));
876       }
877
878       return;
879     }
880
881     SequenceFeature[] sf = sequence.getSequenceFeatures();
882
883     if (sf == null)
884     {
885       return;
886     }
887
888     SequenceFeature[] oldsf = new SequenceFeature[sf.length];
889
890     int cSize = j - i;
891
892     for (int s = 0; s < sf.length; s++)
893     {
894       SequenceFeature copy = new SequenceFeature(sf[s]);
895
896       oldsf[s] = copy;
897
898       if (sf[s].getEnd() < i)
899       {
900         continue;
901       }
902
903       if (sf[s].getBegin() > j)
904       {
905         sf[s].setBegin(copy.getBegin() - cSize);
906         sf[s].setEnd(copy.getEnd() - cSize);
907         continue;
908       }
909
910       if (sf[s].getBegin() >= i)
911       {
912         sf[s].setBegin(i);
913       }
914
915       if (sf[s].getEnd() < j)
916       {
917         sf[s].setEnd(j - 1);
918       }
919
920       sf[s].setEnd(sf[s].getEnd() - (cSize));
921
922       if (sf[s].getBegin() > sf[s].getEnd())
923       {
924         sequence.deleteFeature(sf[s]);
925       }
926     }
927
928     if (command.editedFeatures == null)
929     {
930       command.editedFeatures = new Hashtable();
931     }
932
933     command.editedFeatures.put(seq, oldsf);
934
935   }
936
937   class Edit
938   {
939     public SequenceI[] oldds;
940
941     boolean fullAlignmentHeight = false;
942
943     Hashtable deletedAnnotationRows;
944
945     Hashtable deletedAnnotations;
946
947     Hashtable editedFeatures;
948
949     AlignmentI al;
950
951     int command;
952
953     char[][] string;
954
955     SequenceI[] seqs;
956
957     int[] alIndex;
958
959     int position, number;
960
961     char gapChar;
962
963     Edit(int command, SequenceI[] seqs, int position, int number,
964             char gapChar)
965     {
966       this.command = command;
967       this.seqs = seqs;
968       this.position = position;
969       this.number = number;
970       this.gapChar = gapChar;
971     }
972
973     Edit(int command, SequenceI[] seqs, int position, int number,
974             AlignmentI al)
975     {
976       this.gapChar = al.getGapCharacter();
977       this.command = command;
978       this.seqs = seqs;
979       this.position = position;
980       this.number = number;
981       this.al = al;
982
983       alIndex = new int[seqs.length];
984       for (int i = 0; i < seqs.length; i++)
985       {
986         alIndex[i] = al.findIndex(seqs[i]);
987       }
988
989       fullAlignmentHeight = (al.getHeight() == seqs.length);
990     }
991
992     Edit(int command, SequenceI[] seqs, int position, int number,
993             AlignmentI al, String replace)
994     {
995       this.command = command;
996       this.seqs = seqs;
997       this.position = position;
998       this.number = number;
999       this.al = al;
1000       this.gapChar = al.getGapCharacter();
1001       string = new char[seqs.length][];
1002       for (int i = 0; i < seqs.length; i++)
1003       {
1004         string[i] = replace.toCharArray();
1005       }
1006
1007       fullAlignmentHeight = (al.getHeight() == seqs.length);
1008     }
1009   }
1010 }