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