2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.commands;
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 import jalview.util.ReverseListIterator;
30 import jalview.util.StringUtils;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.Hashtable;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.ListIterator;
47 * Description: Essential information for performing undo and redo for cut/paste
48 * insert/delete gap which can be stored in the HistoryList
52 * Copyright: Copyright (c) 2006
56 * Company: Dundee University
59 * @author not attributable
62 public class EditCommand implements CommandI
69 public Action getUndoAction()
77 public Action getUndoAction()
85 public Action getUndoAction()
93 public Action getUndoAction()
101 public Action getUndoAction()
109 public Action getUndoAction()
114 public abstract Action getUndoAction();
117 private List<Edit> edits = new ArrayList<Edit>();
125 public EditCommand(String description)
127 this.description = description;
130 public EditCommand(String description, Action command, SequenceI[] seqs,
131 int position, int number, AlignmentI al)
133 this.description = description;
134 if (command == Action.CUT || command == Action.PASTE)
136 setEdit(new Edit(command, seqs, position, number, al));
139 performEdit(0, null);
142 public EditCommand(String description, Action command, String replace,
143 SequenceI[] seqs, int position, int number, AlignmentI al)
145 this.description = description;
146 if (command == Action.REPLACE)
148 setEdit(new Edit(command, seqs, position, number, al, replace));
151 performEdit(0, null);
155 * Set the list of edits to the specified item (only).
159 protected void setEdit(Edit e)
166 * Add the given edit command to the stored list of commands. If simply
167 * expanding the range of the last command added, then modify it instead of
168 * adding a new command.
172 public void addEdit(Edit e)
174 if (!expandEdit(edits, e))
181 * Returns true if the new edit is incorporated by updating (expanding the
182 * range of) the last edit on the list, else false. We can 'expand' the last
183 * edit if the new one is the same action, on the same sequences, and acts on
184 * a contiguous range. This is the case where a mouse drag generates a series
185 * of contiguous gap insertions or deletions.
191 protected static boolean expandEdit(List<Edit> edits, Edit e)
193 if (edits == null || edits.isEmpty())
197 Edit lastEdit = edits.get(edits.size() - 1);
198 Action action = e.command;
199 if (lastEdit.command != action)
205 * Both commands must act on the same sequences - compare the underlying
206 * dataset sequences, rather than the aligned sequences, which change as
209 if (lastEdit.seqs.length != e.seqs.length)
213 for (int i = 0; i < e.seqs.length; i++)
215 if (lastEdit.seqs[i].getDatasetSequence() != e.seqs[i]
216 .getDatasetSequence())
223 * Check a contiguous edit; either
225 * <li>a new Insert <n> positions to the right of the last <insert n>, or</li>
226 * <li>a new Delete <n> gaps which is <n> positions to the left of the last
230 boolean contiguous = (action == Action.INSERT_GAP && e.position == lastEdit.position
232 || (action == Action.DELETE_GAP && e.position + e.number == lastEdit.position);
236 * We are just expanding the range of the last edit. For delete gap, also
237 * moving the start position left.
239 lastEdit.number += e.number;
240 lastEdit.seqs = e.seqs;
241 if (action == Action.DELETE_GAP)
251 * Clear the list of stored edit commands.
254 protected void clearEdits()
260 * Returns the i'th stored Edit command.
265 protected Edit getEdit(int i)
267 if (i >= 0 && i < edits.size())
275 final public String getDescription()
287 * Return the alignment for the first edit (or null if no edit).
291 final public AlignmentI getAlignment()
293 return (edits.isEmpty() ? null : edits.get(0).al);
297 * append a new editCommand Note. this shouldn't be called if the edit is an
298 * operation affects more alignment objects than the one referenced in al (for
299 * example, cut or pasting whole sequences). Use the form with an additional
300 * AlignmentI[] views parameter.
309 final public void appendEdit(Action command, SequenceI[] seqs,
311 int number, AlignmentI al, boolean performEdit)
313 appendEdit(command, seqs, position, number, al, performEdit, null);
317 * append a new edit command with a set of alignment views that may be
328 final public void appendEdit(Action command, SequenceI[] seqs,
330 int number, AlignmentI al, boolean performEdit, AlignmentI[] views)
332 Edit edit = new Edit(command, seqs, position, number,
333 al.getGapCharacter());
334 if (al.getHeight() == seqs.length)
337 edit.fullAlignmentHeight = true;
344 performEdit(edit, views);
349 * Overloaded method that accepts an Edit object with additional parameters.
356 final public void appendEdit(Edit edit, AlignmentI al,
357 boolean performEdit, AlignmentI[] views)
359 if (al.getHeight() == edit.seqs.length)
362 edit.fullAlignmentHeight = true;
369 performEdit(edit, views);
374 * Execute all the edit commands, starting at the given commandIndex
376 * @param commandIndex
379 public final void performEdit(int commandIndex, AlignmentI[] views)
381 ListIterator<Edit> iterator = edits.listIterator(commandIndex);
382 while (iterator.hasNext())
384 Edit edit = iterator.next();
385 performEdit(edit, views);
390 * Execute one edit command in all the specified alignment views
395 protected static void performEdit(Edit edit, AlignmentI[] views)
397 switch (edit.command)
415 // TODO:add deleteNuc for UNDO
417 // insertNuc(edits[e]);
425 final public void doCommand(AlignmentI[] views)
427 performEdit(0, views);
431 * Undo the stored list of commands, in reverse order.
434 final public void undoCommand(AlignmentI[] views)
436 ListIterator<Edit> iterator = edits.listIterator(edits.size());
437 while (iterator.hasPrevious())
439 Edit e = iterator.previous();
467 * Insert gap(s) in sequences as specified by the command, and adjust
472 final private static void insertGap(Edit command)
475 for (int s = 0; s < command.seqs.length; s++)
477 command.seqs[s].insertCharAt(command.position,
478 command.number, command.gapChar);
479 // System.out.println("pos: "+command.position+" number: "+command.number);
482 adjustAnnotations(command, true, false, null);
486 // final void insertNuc(Edit command)
489 // for (int s = 0; s < command.seqs.length; s++)
491 // System.out.println("pos: "+command.position+" number: "+command.number);
492 // command.seqs[s].insertCharAt(command.position, command.number,'A');
495 // adjustAnnotations(command, true, false, null);
499 * Delete gap(s) in sequences as specified by the command, and adjust
504 final static private void deleteGap(Edit command)
506 for (int s = 0; s < command.seqs.length; s++)
508 command.seqs[s].deleteChars(command.position, command.position
512 adjustAnnotations(command, false, false, null);
516 * Carry out a Cut action. The cut characters are saved in case Undo is
522 static void cut(Edit command, AlignmentI[] views)
524 boolean seqDeleted = false;
525 command.string = new char[command.seqs.length][];
527 for (int i = 0; i < command.seqs.length; i++)
529 final SequenceI sequence = command.seqs[i];
530 if (sequence.getLength() > command.position)
532 command.string[i] = sequence.getSequence(command.position,
533 command.position + command.number);
534 SequenceI oldds = sequence.getDatasetSequence();
535 if (command.oldds != null && command.oldds[i] != null)
537 // we are redoing an undone cut.
538 sequence.setDatasetSequence(null);
540 sequence.deleteChars(command.position, command.position
542 if (command.oldds != null && command.oldds[i] != null)
544 // oldds entry contains the cut dataset sequence.
545 sequence.setDatasetSequence(command.oldds[i]);
546 command.oldds[i] = oldds;
550 // modify the oldds if necessary
551 if (oldds != sequence.getDatasetSequence()
552 || sequence.getSequenceFeatures() != null)
554 if (command.oldds == null)
556 command.oldds = new SequenceI[command.seqs.length];
558 command.oldds[i] = oldds;
562 sequence.findPosition(command.position),
563 sequence.findPosition(command.position
564 + command.number), false);
569 if (sequence.getLength() < 1)
571 command.al.deleteSequence(sequence);
576 adjustAnnotations(command, false, seqDeleted, views);
580 * Perform the given Paste command. This may be to add cut or copied sequences
581 * to an alignment, or to undo a 'Cut' action on a region of the alignment.
586 static void paste(Edit command, AlignmentI[] views)
590 boolean newDSWasNeeded;
591 int newstart, newend;
592 boolean seqWasDeleted = false;
593 int start = 0, end = 0;
595 for (int i = 0; i < command.seqs.length; i++)
598 newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
599 if (command.seqs[i].getLength() < 1)
601 // ie this sequence was deleted, we need to
602 // readd it to the alignment
603 if (command.alIndex[i] < command.al.getHeight())
605 List<SequenceI> sequences;
606 synchronized (sequences = command.al.getSequences())
608 if (!(command.alIndex[i] < 0))
610 sequences.add(command.alIndex[i], command.seqs[i]);
616 command.al.addSequence(command.seqs[i]);
618 seqWasDeleted = true;
620 newstart = command.seqs[i].getStart();
621 newend = command.seqs[i].getEnd();
623 tmp = new StringBuffer();
624 tmp.append(command.seqs[i].getSequence());
625 // Undo of a delete does not replace original dataset sequence on to
626 // alignment sequence.
628 if (command.string != null && command.string[i] != null)
630 if (command.position >= tmp.length())
632 // This occurs if padding is on, and residues
633 // are removed from end of alignment
634 int length = command.position - tmp.length();
637 tmp.append(command.gapChar);
641 tmp.insert(command.position, command.string[i]);
642 for (int s = 0; s < command.string[i].length; s++)
644 if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
649 start = command.seqs[i].findPosition(command.position);
650 end = command.seqs[i].findPosition(command.position
653 if (command.seqs[i].getStart() == start)
663 command.string[i] = null;
666 command.seqs[i].setSequence(tmp.toString());
667 command.seqs[i].setStart(newstart);
668 command.seqs[i].setEnd(newend);
671 if (command.seqs[i].getDatasetSequence() != null)
676 ds = command.oldds[i];
680 // make a new DS sequence
681 // use new ds mechanism here
682 ds = new Sequence(command.seqs[i].getName(),
683 jalview.analysis.AlignSeq.extractGaps(
684 jalview.util.Comparison.GapChars,
685 command.seqs[i].getSequenceAsString()),
686 command.seqs[i].getStart(), command.seqs[i].getEnd());
687 ds.setDescription(command.seqs[i].getDescription());
689 if (command.oldds == null)
691 command.oldds = new SequenceI[command.seqs.length];
693 command.oldds[i] = command.seqs[i].getDatasetSequence();
694 command.seqs[i].setDatasetSequence(ds);
696 adjustFeatures(command, i, start, end, true);
699 adjustAnnotations(command, true, seqWasDeleted, views);
701 command.string = null;
704 static void replace(Edit command)
708 int start = command.position;
709 int end = command.number;
710 // TODO TUTORIAL - Fix for replacement with different length of sequence (or
712 // TODO Jalview 2.4 bugfix change to an aggregate command - original
713 // sequence string is cut, new string is pasted in.
714 command.number = start + command.string[0].length;
715 for (int i = 0; i < command.seqs.length; i++)
717 boolean newDSWasNeeded = command.oldds != null
718 && command.oldds[i] != null;
721 * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
722 * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
723 * viewport.alignment));
727 * then addHistoryItem(new EditCommand( "Add sequences",
728 * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
731 oldstring = command.seqs[i].getSequenceAsString();
732 tmp = new StringBuffer(oldstring.substring(0, start));
733 tmp.append(command.string[i]);
734 String nogaprep = jalview.analysis.AlignSeq.extractGaps(
735 jalview.util.Comparison.GapChars, new String(
737 int ipos = command.seqs[i].findPosition(start)
738 - command.seqs[i].getStart();
739 tmp.append(oldstring.substring(end));
740 command.seqs[i].setSequence(tmp.toString());
741 command.string[i] = oldstring.substring(start, end).toCharArray();
742 String nogapold = jalview.analysis.AlignSeq.extractGaps(
743 jalview.util.Comparison.GapChars, new String(
745 if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
749 SequenceI oldds = command.seqs[i].getDatasetSequence();
750 command.seqs[i].setDatasetSequence(command.oldds[i]);
751 command.oldds[i] = oldds;
755 if (command.oldds == null)
757 command.oldds = new SequenceI[command.seqs.length];
759 command.oldds[i] = command.seqs[i].getDatasetSequence();
760 SequenceI newds = new Sequence(
761 command.seqs[i].getDatasetSequence());
762 String fullseq, osp = newds.getSequenceAsString();
763 fullseq = osp.substring(0, ipos) + nogaprep
764 + osp.substring(ipos + nogaprep.length());
765 newds.setSequence(fullseq.toUpperCase());
766 // TODO: JAL-1131 ensure newly created dataset sequence is added to
768 // dataset sequences associated with the alignment.
769 // TODO: JAL-1131 fix up any annotation associated with new dataset
770 // sequence to ensure that original sequence/annotation relationships
772 command.seqs[i].setDatasetSequence(newds);
781 final static void adjustAnnotations(Edit command, boolean insert,
782 boolean modifyVisibility, AlignmentI[] views)
784 AlignmentAnnotation[] annotations = null;
786 if (modifyVisibility && !insert)
788 // only occurs if a sequence was added or deleted.
789 command.deletedAnnotationRows = new Hashtable<SequenceI, AlignmentAnnotation[]>();
791 if (command.fullAlignmentHeight)
793 annotations = command.al.getAlignmentAnnotation();
798 AlignmentAnnotation[] tmp;
799 for (int s = 0; s < command.seqs.length; s++)
801 if (modifyVisibility)
803 // Rows are only removed or added to sequence object.
807 tmp = command.seqs[s].getAnnotation();
810 int alen = tmp.length;
811 for (int aa = 0; aa < tmp.length; aa++)
813 if (!command.al.deleteAnnotation(tmp[aa]))
815 // strip out annotation not in the current al (will be put
816 // back on insert in all views)
821 command.seqs[s].setAlignmentAnnotation(null);
822 if (alen != tmp.length)
824 // save the non-null annotation references only
825 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
826 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
830 saved[aapos++] = tmp[aa];
835 command.deletedAnnotationRows.put(command.seqs[s], saved);
836 // and then remove any annotation in the other views
837 for (int alview = 0; views != null && alview < views.length; alview++)
839 if (views[alview] != command.al)
841 AlignmentAnnotation[] toremove = views[alview]
842 .getAlignmentAnnotation();
843 if (toremove == null || toremove.length == 0)
847 // remove any alignment annotation on this sequence that's
848 // on that alignment view.
849 for (int aa = 0; aa < toremove.length; aa++)
851 if (toremove[aa].sequenceRef == command.seqs[s])
853 views[alview].deleteAnnotation(toremove[aa]);
861 // save all the annotation
862 command.deletedAnnotationRows.put(command.seqs[s], tmp);
869 if (command.deletedAnnotationRows != null
870 && command.deletedAnnotationRows
871 .containsKey(command.seqs[s]))
873 AlignmentAnnotation[] revealed = command.deletedAnnotationRows
874 .get(command.seqs[s]);
875 command.seqs[s].setAlignmentAnnotation(revealed);
876 if (revealed != null)
878 for (int aa = 0; aa < revealed.length; aa++)
880 // iterate through al adding original annotation
881 command.al.addAnnotation(revealed[aa]);
883 for (int aa = 0; aa < revealed.length; aa++)
885 command.al.setAnnotationIndex(revealed[aa], aa);
887 // and then duplicate added annotation on every other alignment
889 for (int vnum = 0; views != null && vnum < views.length; vnum++)
891 if (views[vnum] != command.al)
893 int avwidth = views[vnum].getWidth() + 1;
894 // duplicate in this view
895 for (int a = 0; a < revealed.length; a++)
897 AlignmentAnnotation newann = new AlignmentAnnotation(
899 command.seqs[s].addAlignmentAnnotation(newann);
900 newann.padAnnotation(avwidth);
901 views[vnum].addAnnotation(newann);
902 views[vnum].setAnnotationIndex(newann, a);
912 if (command.seqs[s].getAnnotation() == null)
919 annotations = command.seqs[s].getAnnotation();
923 tmp = new AlignmentAnnotation[aSize
924 + command.seqs[s].getAnnotation().length];
926 System.arraycopy(annotations, 0, tmp, 0, aSize);
928 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
929 command.seqs[s].getAnnotation().length);
933 aSize = annotations.length;
937 if (annotations == null)
944 command.deletedAnnotations = new Hashtable<String, Annotation[]>();
949 for (int a = 0; a < annotations.length; a++)
951 if (annotations[a].autoCalculated
952 || annotations[a].annotations == null)
959 aSize = annotations[a].annotations.length;
962 temp = new Annotation[aSize + command.number];
963 if (annotations[a].padGaps)
965 for (int aa = 0; aa < temp.length; aa++)
967 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
973 if (command.position < aSize)
975 if (command.position + command.number >= aSize)
981 tSize = aSize - command.number;
993 temp = new Annotation[tSize];
998 if (command.position < annotations[a].annotations.length)
1000 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1003 if (command.deletedAnnotations != null
1004 && command.deletedAnnotations
1005 .containsKey(annotations[a].annotationId))
1007 Annotation[] restore = command.deletedAnnotations
1008 .get(annotations[a].annotationId);
1010 System.arraycopy(restore, 0, temp, command.position,
1015 System.arraycopy(annotations[a].annotations, command.position,
1016 temp, command.position + command.number, aSize
1017 - command.position);
1021 if (command.deletedAnnotations != null
1022 && command.deletedAnnotations
1023 .containsKey(annotations[a].annotationId))
1025 Annotation[] restore = command.deletedAnnotations
1026 .get(annotations[a].annotationId);
1028 temp = new Annotation[annotations[a].annotations.length
1030 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1031 annotations[a].annotations.length);
1032 System.arraycopy(restore, 0, temp,
1033 annotations[a].annotations.length, restore.length);
1037 temp = annotations[a].annotations;
1043 if (tSize != aSize || command.position < 2)
1045 int copylen = Math.min(command.position,
1046 annotations[a].annotations.length);
1049 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1050 copylen); // command.position);
1053 Annotation[] deleted = new Annotation[command.number];
1054 if (copylen >= command.position)
1056 copylen = Math.min(command.number,
1057 annotations[a].annotations.length - command.position);
1060 System.arraycopy(annotations[a].annotations,
1061 command.position, deleted, 0, copylen); // command.number);
1065 command.deletedAnnotations.put(annotations[a].annotationId,
1067 if (annotations[a].annotations.length > command.position
1070 System.arraycopy(annotations[a].annotations, command.position
1071 + command.number, temp, command.position,
1072 annotations[a].annotations.length - command.position
1073 - command.number); // aSize
1078 int dSize = aSize - command.position;
1082 Annotation[] deleted = new Annotation[command.number];
1083 System.arraycopy(annotations[a].annotations, command.position,
1086 command.deletedAnnotations.put(annotations[a].annotationId,
1089 tSize = Math.min(annotations[a].annotations.length,
1091 temp = new Annotation[tSize];
1092 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
1096 temp = annotations[a].annotations;
1101 annotations[a].annotations = temp;
1105 final static void adjustFeatures(Edit command, int index, int i, int j,
1108 SequenceI seq = command.seqs[index];
1109 SequenceI sequence = seq.getDatasetSequence();
1110 if (sequence == null)
1117 if (command.editedFeatures != null
1118 && command.editedFeatures.containsKey(seq))
1120 sequence.setSequenceFeatures(command.editedFeatures
1127 SequenceFeature[] sf = sequence.getSequenceFeatures();
1134 SequenceFeature[] oldsf = new SequenceFeature[sf.length];
1138 for (int s = 0; s < sf.length; s++)
1140 SequenceFeature copy = new SequenceFeature(sf[s]);
1144 if (sf[s].getEnd() < i)
1149 if (sf[s].getBegin() > j)
1151 sf[s].setBegin(copy.getBegin() - cSize);
1152 sf[s].setEnd(copy.getEnd() - cSize);
1156 if (sf[s].getBegin() >= i)
1161 if (sf[s].getEnd() < j)
1163 sf[s].setEnd(j - 1);
1166 sf[s].setEnd(sf[s].getEnd() - (cSize));
1168 if (sf[s].getBegin() > sf[s].getEnd())
1170 sequence.deleteFeature(sf[s]);
1174 if (command.editedFeatures == null)
1176 command.editedFeatures = new Hashtable<SequenceI, SequenceFeature[]>();
1179 command.editedFeatures.put(seq, oldsf);
1184 * Returns the list of edit commands wrapped by this object.
1188 public List<Edit> getEdits()
1194 * Returns a map whose keys are the dataset sequences, and values their
1195 * aligned sequences before the command edit list was applied. The aligned
1196 * sequences are copies, which may be updated without affecting the originals.
1198 * The command holds references to the aligned sequences (after editing). If
1199 * the command is an 'undo',then the prior state is simply the aligned state.
1200 * Otherwise, we have to derive the prior state by working backwards through
1201 * the edit list to infer the aligned sequences before editing.
1203 * Note: an alternative solution would be to cache the 'before' state of each
1204 * edit, but this would be expensive in space in the common case that the
1205 * original is never needed (edits are not mirrored).
1208 * @throws IllegalStateException
1209 * on detecting an edit command of a type that can't be unwound
1211 public Map<SequenceI, SequenceI> priorState(boolean forUndo)
1213 Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
1214 if (getEdits() == null)
1220 for (Edit e : getEdits())
1222 for (SequenceI seq : e.getSequences())
1224 SequenceI ds = seq.getDatasetSequence();
1225 SequenceI preEdit = result.get(ds);
1226 if (preEdit == null)
1228 preEdit = new Sequence("", seq.getSequenceAsString());
1229 preEdit.setDatasetSequence(ds);
1230 result.put(ds, preEdit);
1238 * Work backwards through the edit list, deriving the sequences before each
1239 * was applied. The final result is the sequence set before any edits.
1241 Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
1242 while (edits.hasNext())
1244 Edit oldEdit = edits.next();
1245 Action action = oldEdit.getAction();
1246 int position = oldEdit.getPosition();
1247 int number = oldEdit.getNumber();
1248 final char gap = oldEdit.getGapCharacter();
1249 for (SequenceI seq : oldEdit.getSequences())
1251 SequenceI ds = seq.getDatasetSequence();
1252 SequenceI preEdit = result.get(ds);
1253 if (preEdit == null)
1255 preEdit = new Sequence("", seq.getSequenceAsString());
1256 preEdit.setDatasetSequence(ds);
1257 result.put(ds, preEdit);
1260 * 'Undo' this edit action on the sequence (updating the value in the
1265 if (action == Action.DELETE_GAP)
1267 preEdit.setSequence(new String(StringUtils.insertCharAt(
1268 preEdit.getSequence(), position,
1271 else if (action == Action.INSERT_GAP)
1273 preEdit.setSequence(new String(StringUtils.deleteChars(
1274 preEdit.getSequence(), position, position + number)));
1278 System.err.println("Can't undo edit action " + action);
1279 // throw new IllegalStateException("Can't undo edit action " +
1290 public SequenceI[] oldds;
1292 boolean fullAlignmentHeight = false;
1294 Hashtable<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
1296 Hashtable<String, Annotation[]> deletedAnnotations;
1298 Hashtable<SequenceI, SequenceFeature[]> editedFeatures;
1310 int position, number;
1314 public Edit(Action command, SequenceI[] seqs, int position, int number,
1317 this.command = command;
1319 this.position = position;
1320 this.number = number;
1321 this.gapChar = gapChar;
1324 Edit(Action command, SequenceI[] seqs, int position, int number,
1327 this.gapChar = al.getGapCharacter();
1328 this.command = command;
1330 this.position = position;
1331 this.number = number;
1334 alIndex = new int[seqs.length];
1335 for (int i = 0; i < seqs.length; i++)
1337 alIndex[i] = al.findIndex(seqs[i]);
1340 fullAlignmentHeight = (al.getHeight() == seqs.length);
1343 Edit(Action command, SequenceI[] seqs, int position, int number,
1344 AlignmentI al, String replace)
1346 this.command = command;
1348 this.position = position;
1349 this.number = number;
1351 this.gapChar = al.getGapCharacter();
1352 string = new char[seqs.length][];
1353 for (int i = 0; i < seqs.length; i++)
1355 string[i] = replace.toCharArray();
1358 fullAlignmentHeight = (al.getHeight() == seqs.length);
1361 public SequenceI[] getSequences()
1366 public int getPosition()
1371 public Action getAction()
1376 public int getNumber()
1381 public char getGapCharacter()
1388 * Returns an iterator over the list of edit commands which traverses the list
1389 * either forwards or backwards.
1394 public Iterator<Edit> getEditIterator(boolean forwards)
1398 return getEdits().iterator();
1402 return new ReverseListIterator<Edit>(getEdits());