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