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.analysis.AlignSeq;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.Annotation;
27 import jalview.datamodel.Sequence;
28 import jalview.datamodel.SequenceFeature;
29 import jalview.datamodel.SequenceI;
30 import jalview.schemes.ResidueProperties;
31 import jalview.util.Comparison;
32 import jalview.util.ReverseListIterator;
33 import jalview.util.StringUtils;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.Hashtable;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.ListIterator;
50 * Description: Essential information for performing undo and redo for cut/paste
51 * insert/delete gap which can be stored in the HistoryList
55 * Copyright: Copyright (c) 2006
59 * Company: Dundee University
62 * @author not attributable
65 public class EditCommand implements CommandI
72 public Action getUndoAction()
80 public Action getUndoAction()
88 public Action getUndoAction()
96 public Action getUndoAction()
104 public Action getUndoAction()
112 public Action getUndoAction()
117 public abstract Action getUndoAction();
120 private List<Edit> edits = new ArrayList<Edit>();
128 public EditCommand(String description)
130 this.description = description;
133 public EditCommand(String description, Action command, SequenceI[] seqs,
134 int position, int number, AlignmentI al)
136 this.description = description;
137 if (command == Action.CUT || command == Action.PASTE)
139 setEdit(new Edit(command, seqs, position, number, al));
142 performEdit(0, null);
145 public EditCommand(String description, Action command, String replace,
146 SequenceI[] seqs, int position, int number, AlignmentI al)
148 this.description = description;
149 if (command == Action.REPLACE)
151 setEdit(new Edit(command, seqs, position, number, al, replace));
154 performEdit(0, null);
158 * Set the list of edits to the specified item (only).
162 protected void setEdit(Edit e)
169 * Add the given edit command to the stored list of commands. If simply
170 * expanding the range of the last command added, then modify it instead of
171 * adding a new command.
175 public void addEdit(Edit e)
177 if (!expandEdit(edits, e))
184 * Returns true if the new edit is incorporated by updating (expanding the
185 * range of) the last edit on the list, else false. We can 'expand' the last
186 * edit if the new one is the same action, on the same sequences, and acts on
187 * a contiguous range. This is the case where a mouse drag generates a series
188 * of contiguous gap insertions or deletions.
194 protected static boolean expandEdit(List<Edit> edits, Edit e)
196 if (edits == null || edits.isEmpty())
200 Edit lastEdit = edits.get(edits.size() - 1);
201 Action action = e.command;
202 if (lastEdit.command != action)
208 * Both commands must act on the same sequences - compare the underlying
209 * dataset sequences, rather than the aligned sequences, which change as
212 if (lastEdit.seqs.length != e.seqs.length)
216 for (int i = 0; i < e.seqs.length; i++)
218 if (lastEdit.seqs[i].getDatasetSequence() != e.seqs[i]
219 .getDatasetSequence())
226 * Check a contiguous edit; either
228 * <li>a new Insert <n> positions to the right of the last <insert n>, or</li>
229 * <li>a new Delete <n> gaps which is <n> positions to the left of the last
233 boolean contiguous = (action == Action.INSERT_GAP && e.position == lastEdit.position
235 || (action == Action.DELETE_GAP && e.position + e.number == lastEdit.position);
239 * We are just expanding the range of the last edit. For delete gap, also
240 * moving the start position left.
242 lastEdit.number += e.number;
243 lastEdit.seqs = e.seqs;
244 if (action == Action.DELETE_GAP)
254 * Clear the list of stored edit commands.
257 protected void clearEdits()
263 * Returns the i'th stored Edit command.
268 protected Edit getEdit(int i)
270 if (i >= 0 && i < edits.size())
278 final public String getDescription()
290 * Return the alignment for the first edit (or null if no edit).
294 final public AlignmentI getAlignment()
296 return (edits.isEmpty() ? null : edits.get(0).al);
300 * append a new editCommand Note. this shouldn't be called if the edit is an
301 * operation affects more alignment objects than the one referenced in al (for
302 * example, cut or pasting whole sequences). Use the form with an additional
303 * AlignmentI[] views parameter.
312 final public void appendEdit(Action command, SequenceI[] seqs,
314 int number, AlignmentI al, boolean performEdit)
316 appendEdit(command, seqs, position, number, al, performEdit, null);
320 * append a new edit command with a set of alignment views that may be
331 final public void appendEdit(Action command, SequenceI[] seqs,
333 int number, AlignmentI al, boolean performEdit, AlignmentI[] views)
335 Edit edit = new Edit(command, seqs, position, number,
336 al.getGapCharacter());
337 if (al.getHeight() == seqs.length)
340 edit.fullAlignmentHeight = true;
347 performEdit(edit, views);
352 * Overloaded method that accepts an Edit object with additional parameters.
359 final public void appendEdit(Edit edit, AlignmentI al,
360 boolean performEdit, AlignmentI[] views)
362 if (al.getHeight() == edit.seqs.length)
365 edit.fullAlignmentHeight = true;
372 performEdit(edit, views);
377 * Execute all the edit commands, starting at the given commandIndex
379 * @param commandIndex
382 public final void performEdit(int commandIndex, AlignmentI[] views)
384 ListIterator<Edit> iterator = edits.listIterator(commandIndex);
385 while (iterator.hasNext())
387 Edit edit = iterator.next();
388 performEdit(edit, views);
393 * Execute one edit command in all the specified alignment views
398 protected static void performEdit(Edit edit, AlignmentI[] views)
400 switch (edit.command)
418 // TODO:add deleteNuc for UNDO
420 // insertNuc(edits[e]);
428 final public void doCommand(AlignmentI[] views)
430 performEdit(0, views);
434 * Undo the stored list of commands, in reverse order.
437 final public void undoCommand(AlignmentI[] views)
439 ListIterator<Edit> iterator = edits.listIterator(edits.size());
440 while (iterator.hasPrevious())
442 Edit e = iterator.previous();
470 * Insert gap(s) in sequences as specified by the command, and adjust
475 final private static void insertGap(Edit command)
478 for (int s = 0; s < command.seqs.length; s++)
480 command.seqs[s].insertCharAt(command.position,
481 command.number, command.gapChar);
482 // System.out.println("pos: "+command.position+" number: "+command.number);
485 adjustAnnotations(command, true, false, null);
489 // final void insertNuc(Edit command)
492 // for (int s = 0; s < command.seqs.length; s++)
494 // System.out.println("pos: "+command.position+" number: "+command.number);
495 // command.seqs[s].insertCharAt(command.position, command.number,'A');
498 // adjustAnnotations(command, true, false, null);
502 * Delete gap(s) in sequences as specified by the command, and adjust
507 final static private void deleteGap(Edit command)
509 for (int s = 0; s < command.seqs.length; s++)
511 command.seqs[s].deleteChars(command.position, command.position
515 adjustAnnotations(command, false, false, null);
519 * Carry out a Cut action. The cut characters are saved in case Undo is
525 static void cut(Edit command, AlignmentI[] views)
527 boolean seqDeleted = false;
528 command.string = new char[command.seqs.length][];
530 for (int i = 0; i < command.seqs.length; i++)
532 final SequenceI sequence = command.seqs[i];
533 if (sequence.getLength() > command.position)
535 command.string[i] = sequence.getSequence(command.position,
536 command.position + command.number);
537 SequenceI oldds = sequence.getDatasetSequence();
538 if (command.oldds != null && command.oldds[i] != null)
540 // we are redoing an undone cut.
541 sequence.setDatasetSequence(null);
543 sequence.deleteChars(command.position, command.position
545 if (command.oldds != null && command.oldds[i] != null)
547 // oldds entry contains the cut dataset sequence.
548 sequence.setDatasetSequence(command.oldds[i]);
549 command.oldds[i] = oldds;
553 // modify the oldds if necessary
554 if (oldds != sequence.getDatasetSequence()
555 || sequence.getSequenceFeatures() != null)
557 if (command.oldds == null)
559 command.oldds = new SequenceI[command.seqs.length];
561 command.oldds[i] = oldds;
565 sequence.findPosition(command.position),
566 sequence.findPosition(command.position
567 + command.number), false);
572 if (sequence.getLength() < 1)
574 command.al.deleteSequence(sequence);
579 adjustAnnotations(command, false, seqDeleted, views);
583 * Perform the given Paste command. This may be to add cut or copied sequences
584 * to an alignment, or to undo a 'Cut' action on a region of the alignment.
589 static void paste(Edit command, AlignmentI[] views)
593 boolean newDSWasNeeded;
594 int newstart, newend;
595 boolean seqWasDeleted = false;
596 int start = 0, end = 0;
598 for (int i = 0; i < command.seqs.length; i++)
601 newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
602 if (command.seqs[i].getLength() < 1)
604 // ie this sequence was deleted, we need to
605 // readd it to the alignment
606 if (command.alIndex[i] < command.al.getHeight())
608 List<SequenceI> sequences;
609 synchronized (sequences = command.al.getSequences())
611 if (!(command.alIndex[i] < 0))
613 sequences.add(command.alIndex[i], command.seqs[i]);
619 command.al.addSequence(command.seqs[i]);
621 seqWasDeleted = true;
623 newstart = command.seqs[i].getStart();
624 newend = command.seqs[i].getEnd();
626 tmp = new StringBuffer();
627 tmp.append(command.seqs[i].getSequence());
628 // Undo of a delete does not replace original dataset sequence on to
629 // alignment sequence.
631 if (command.string != null && command.string[i] != null)
633 if (command.position >= tmp.length())
635 // This occurs if padding is on, and residues
636 // are removed from end of alignment
637 int length = command.position - tmp.length();
640 tmp.append(command.gapChar);
644 tmp.insert(command.position, command.string[i]);
645 for (int s = 0; s < command.string[i].length; s++)
647 if (ResidueProperties.aaIndex[command.string[i][s]] != 23)
652 start = command.seqs[i].findPosition(command.position);
653 end = command.seqs[i].findPosition(command.position
656 if (command.seqs[i].getStart() == start)
666 command.string[i] = null;
669 command.seqs[i].setSequence(tmp.toString());
670 command.seqs[i].setStart(newstart);
671 command.seqs[i].setEnd(newend);
674 if (command.seqs[i].getDatasetSequence() != null)
679 ds = command.oldds[i];
683 // make a new DS sequence
684 // use new ds mechanism here
685 ds = new Sequence(command.seqs[i].getName(),
686 AlignSeq.extractGaps(
688 command.seqs[i].getSequenceAsString()),
689 command.seqs[i].getStart(), command.seqs[i].getEnd());
690 ds.setDescription(command.seqs[i].getDescription());
692 if (command.oldds == null)
694 command.oldds = new SequenceI[command.seqs.length];
696 command.oldds[i] = command.seqs[i].getDatasetSequence();
697 command.seqs[i].setDatasetSequence(ds);
699 adjustFeatures(command, i, start, end, true);
702 adjustAnnotations(command, true, seqWasDeleted, views);
704 command.string = null;
707 static void replace(Edit command)
711 int start = command.position;
712 int end = command.number;
713 // TODO TUTORIAL - Fix for replacement with different length of sequence (or
715 // TODO Jalview 2.4 bugfix change to an aggregate command - original
716 // sequence string is cut, new string is pasted in.
717 command.number = start + command.string[0].length;
718 for (int i = 0; i < command.seqs.length; i++)
720 boolean newDSWasNeeded = command.oldds != null
721 && command.oldds[i] != null;
724 * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
725 * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
726 * viewport.alignment));
730 * then addHistoryItem(new EditCommand( "Add sequences",
731 * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
734 oldstring = command.seqs[i].getSequenceAsString();
735 tmp = new StringBuffer(oldstring.substring(0, start));
736 tmp.append(command.string[i]);
737 String nogaprep = AlignSeq.extractGaps(
738 Comparison.GapChars, new String(
740 int ipos = command.seqs[i].findPosition(start)
741 - command.seqs[i].getStart();
742 tmp.append(oldstring.substring(end));
743 command.seqs[i].setSequence(tmp.toString());
744 command.string[i] = oldstring.substring(start, end).toCharArray();
745 String nogapold = AlignSeq.extractGaps(
746 Comparison.GapChars, new String(
748 if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
752 SequenceI oldds = command.seqs[i].getDatasetSequence();
753 command.seqs[i].setDatasetSequence(command.oldds[i]);
754 command.oldds[i] = oldds;
758 if (command.oldds == null)
760 command.oldds = new SequenceI[command.seqs.length];
762 command.oldds[i] = command.seqs[i].getDatasetSequence();
763 SequenceI newds = new Sequence(
764 command.seqs[i].getDatasetSequence());
765 String fullseq, osp = newds.getSequenceAsString();
766 fullseq = osp.substring(0, ipos) + nogaprep
767 + osp.substring(ipos + nogaprep.length());
768 newds.setSequence(fullseq.toUpperCase());
769 // TODO: JAL-1131 ensure newly created dataset sequence is added to
771 // dataset sequences associated with the alignment.
772 // TODO: JAL-1131 fix up any annotation associated with new dataset
773 // sequence to ensure that original sequence/annotation relationships
775 command.seqs[i].setDatasetSequence(newds);
784 final static void adjustAnnotations(Edit command, boolean insert,
785 boolean modifyVisibility, AlignmentI[] views)
787 AlignmentAnnotation[] annotations = null;
789 if (modifyVisibility && !insert)
791 // only occurs if a sequence was added or deleted.
792 command.deletedAnnotationRows = new Hashtable<SequenceI, AlignmentAnnotation[]>();
794 if (command.fullAlignmentHeight)
796 annotations = command.al.getAlignmentAnnotation();
801 AlignmentAnnotation[] tmp;
802 for (int s = 0; s < command.seqs.length; s++)
804 if (modifyVisibility)
806 // Rows are only removed or added to sequence object.
810 tmp = command.seqs[s].getAnnotation();
813 int alen = tmp.length;
814 for (int aa = 0; aa < tmp.length; aa++)
816 if (!command.al.deleteAnnotation(tmp[aa]))
818 // strip out annotation not in the current al (will be put
819 // back on insert in all views)
824 command.seqs[s].setAlignmentAnnotation(null);
825 if (alen != tmp.length)
827 // save the non-null annotation references only
828 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
829 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
833 saved[aapos++] = tmp[aa];
838 command.deletedAnnotationRows.put(command.seqs[s], saved);
839 // and then remove any annotation in the other views
840 for (int alview = 0; views != null && alview < views.length; alview++)
842 if (views[alview] != command.al)
844 AlignmentAnnotation[] toremove = views[alview]
845 .getAlignmentAnnotation();
846 if (toremove == null || toremove.length == 0)
850 // remove any alignment annotation on this sequence that's
851 // on that alignment view.
852 for (int aa = 0; aa < toremove.length; aa++)
854 if (toremove[aa].sequenceRef == command.seqs[s])
856 views[alview].deleteAnnotation(toremove[aa]);
864 // save all the annotation
865 command.deletedAnnotationRows.put(command.seqs[s], tmp);
872 if (command.deletedAnnotationRows != null
873 && command.deletedAnnotationRows
874 .containsKey(command.seqs[s]))
876 AlignmentAnnotation[] revealed = command.deletedAnnotationRows
877 .get(command.seqs[s]);
878 command.seqs[s].setAlignmentAnnotation(revealed);
879 if (revealed != null)
881 for (int aa = 0; aa < revealed.length; aa++)
883 // iterate through al adding original annotation
884 command.al.addAnnotation(revealed[aa]);
886 for (int aa = 0; aa < revealed.length; aa++)
888 command.al.setAnnotationIndex(revealed[aa], aa);
890 // and then duplicate added annotation on every other alignment
892 for (int vnum = 0; views != null && vnum < views.length; vnum++)
894 if (views[vnum] != command.al)
896 int avwidth = views[vnum].getWidth() + 1;
897 // duplicate in this view
898 for (int a = 0; a < revealed.length; a++)
900 AlignmentAnnotation newann = new AlignmentAnnotation(
902 command.seqs[s].addAlignmentAnnotation(newann);
903 newann.padAnnotation(avwidth);
904 views[vnum].addAnnotation(newann);
905 views[vnum].setAnnotationIndex(newann, a);
915 if (command.seqs[s].getAnnotation() == null)
922 annotations = command.seqs[s].getAnnotation();
926 tmp = new AlignmentAnnotation[aSize
927 + command.seqs[s].getAnnotation().length];
929 System.arraycopy(annotations, 0, tmp, 0, aSize);
931 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
932 command.seqs[s].getAnnotation().length);
936 aSize = annotations.length;
940 if (annotations == null)
947 command.deletedAnnotations = new Hashtable<String, Annotation[]>();
952 for (int a = 0; a < annotations.length; a++)
954 if (annotations[a].autoCalculated
955 || annotations[a].annotations == null)
962 aSize = annotations[a].annotations.length;
965 temp = new Annotation[aSize + command.number];
966 if (annotations[a].padGaps)
968 for (int aa = 0; aa < temp.length; aa++)
970 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
976 if (command.position < aSize)
978 if (command.position + command.number >= aSize)
984 tSize = aSize - command.number;
996 temp = new Annotation[tSize];
1001 if (command.position < annotations[a].annotations.length)
1003 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1006 if (command.deletedAnnotations != null
1007 && command.deletedAnnotations
1008 .containsKey(annotations[a].annotationId))
1010 Annotation[] restore = command.deletedAnnotations
1011 .get(annotations[a].annotationId);
1013 System.arraycopy(restore, 0, temp, command.position,
1018 System.arraycopy(annotations[a].annotations, command.position,
1019 temp, command.position + command.number, aSize
1020 - command.position);
1024 if (command.deletedAnnotations != null
1025 && command.deletedAnnotations
1026 .containsKey(annotations[a].annotationId))
1028 Annotation[] restore = command.deletedAnnotations
1029 .get(annotations[a].annotationId);
1031 temp = new Annotation[annotations[a].annotations.length
1033 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1034 annotations[a].annotations.length);
1035 System.arraycopy(restore, 0, temp,
1036 annotations[a].annotations.length, restore.length);
1040 temp = annotations[a].annotations;
1046 if (tSize != aSize || command.position < 2)
1048 int copylen = Math.min(command.position,
1049 annotations[a].annotations.length);
1052 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1053 copylen); // command.position);
1056 Annotation[] deleted = new Annotation[command.number];
1057 if (copylen >= command.position)
1059 copylen = Math.min(command.number,
1060 annotations[a].annotations.length - command.position);
1063 System.arraycopy(annotations[a].annotations,
1064 command.position, deleted, 0, copylen); // command.number);
1068 command.deletedAnnotations.put(annotations[a].annotationId,
1070 if (annotations[a].annotations.length > command.position
1073 System.arraycopy(annotations[a].annotations, command.position
1074 + command.number, temp, command.position,
1075 annotations[a].annotations.length - command.position
1076 - command.number); // aSize
1081 int dSize = aSize - command.position;
1085 Annotation[] deleted = new Annotation[command.number];
1086 System.arraycopy(annotations[a].annotations, command.position,
1089 command.deletedAnnotations.put(annotations[a].annotationId,
1092 tSize = Math.min(annotations[a].annotations.length,
1094 temp = new Annotation[tSize];
1095 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
1099 temp = annotations[a].annotations;
1104 annotations[a].annotations = temp;
1108 final static void adjustFeatures(Edit command, int index, int i, int j,
1111 SequenceI seq = command.seqs[index];
1112 SequenceI sequence = seq.getDatasetSequence();
1113 if (sequence == null)
1120 if (command.editedFeatures != null
1121 && command.editedFeatures.containsKey(seq))
1123 sequence.setSequenceFeatures(command.editedFeatures
1130 SequenceFeature[] sf = sequence.getSequenceFeatures();
1137 SequenceFeature[] oldsf = new SequenceFeature[sf.length];
1141 for (int s = 0; s < sf.length; s++)
1143 SequenceFeature copy = new SequenceFeature(sf[s]);
1147 if (sf[s].getEnd() < i)
1152 if (sf[s].getBegin() > j)
1154 sf[s].setBegin(copy.getBegin() - cSize);
1155 sf[s].setEnd(copy.getEnd() - cSize);
1159 if (sf[s].getBegin() >= i)
1164 if (sf[s].getEnd() < j)
1166 sf[s].setEnd(j - 1);
1169 sf[s].setEnd(sf[s].getEnd() - (cSize));
1171 if (sf[s].getBegin() > sf[s].getEnd())
1173 sequence.deleteFeature(sf[s]);
1177 if (command.editedFeatures == null)
1179 command.editedFeatures = new Hashtable<SequenceI, SequenceFeature[]>();
1182 command.editedFeatures.put(seq, oldsf);
1187 * Returns the list of edit commands wrapped by this object.
1191 public List<Edit> getEdits()
1197 * Returns a map whose keys are the dataset sequences, and values their
1198 * aligned sequences before the command edit list was applied. The aligned
1199 * sequences are copies, which may be updated without affecting the originals.
1201 * The command holds references to the aligned sequences (after editing). If
1202 * the command is an 'undo',then the prior state is simply the aligned state.
1203 * Otherwise, we have to derive the prior state by working backwards through
1204 * the edit list to infer the aligned sequences before editing.
1206 * Note: an alternative solution would be to cache the 'before' state of each
1207 * edit, but this would be expensive in space in the common case that the
1208 * original is never needed (edits are not mirrored).
1211 * @throws IllegalStateException
1212 * on detecting an edit command of a type that can't be unwound
1214 public Map<SequenceI, SequenceI> priorState(boolean forUndo)
1216 Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
1217 if (getEdits() == null)
1223 for (Edit e : getEdits())
1225 for (SequenceI seq : e.getSequences())
1227 SequenceI ds = seq.getDatasetSequence();
1228 SequenceI preEdit = result.get(ds);
1229 if (preEdit == null)
1231 preEdit = new Sequence("", seq.getSequenceAsString());
1232 preEdit.setDatasetSequence(ds);
1233 result.put(ds, preEdit);
1241 * Work backwards through the edit list, deriving the sequences before each
1242 * was applied. The final result is the sequence set before any edits.
1244 Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
1245 while (edits.hasNext())
1247 Edit oldEdit = edits.next();
1248 Action action = oldEdit.getAction();
1249 int position = oldEdit.getPosition();
1250 int number = oldEdit.getNumber();
1251 final char gap = oldEdit.getGapCharacter();
1252 for (SequenceI seq : oldEdit.getSequences())
1254 SequenceI ds = seq.getDatasetSequence();
1255 SequenceI preEdit = result.get(ds);
1256 if (preEdit == null)
1258 preEdit = new Sequence("", seq.getSequenceAsString());
1259 preEdit.setDatasetSequence(ds);
1260 result.put(ds, preEdit);
1263 * 'Undo' this edit action on the sequence (updating the value in the
1268 if (action == Action.DELETE_GAP)
1270 preEdit.setSequence(new String(StringUtils.insertCharAt(
1271 preEdit.getSequence(), position,
1274 else if (action == Action.INSERT_GAP)
1276 preEdit.setSequence(new String(StringUtils.deleteChars(
1277 preEdit.getSequence(), position, position + number)));
1281 System.err.println("Can't undo edit action " + action);
1282 // throw new IllegalStateException("Can't undo edit action " +
1293 public SequenceI[] oldds;
1295 boolean fullAlignmentHeight = false;
1297 Hashtable<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
1299 Hashtable<String, Annotation[]> deletedAnnotations;
1301 Hashtable<SequenceI, SequenceFeature[]> editedFeatures;
1313 int position, number;
1317 public Edit(Action command, SequenceI[] seqs, int position, int number,
1320 this.command = command;
1322 this.position = position;
1323 this.number = number;
1324 this.gapChar = gapChar;
1327 Edit(Action command, SequenceI[] seqs, int position, int number,
1330 this.gapChar = al.getGapCharacter();
1331 this.command = command;
1333 this.position = position;
1334 this.number = number;
1337 alIndex = new int[seqs.length];
1338 for (int i = 0; i < seqs.length; i++)
1340 alIndex[i] = al.findIndex(seqs[i]);
1343 fullAlignmentHeight = (al.getHeight() == seqs.length);
1346 Edit(Action command, SequenceI[] seqs, int position, int number,
1347 AlignmentI al, String replace)
1349 this.command = command;
1351 this.position = position;
1352 this.number = number;
1354 this.gapChar = al.getGapCharacter();
1355 string = new char[seqs.length][];
1356 for (int i = 0; i < seqs.length; i++)
1358 string[i] = replace.toCharArray();
1361 fullAlignmentHeight = (al.getHeight() == seqs.length);
1364 public SequenceI[] getSequences()
1369 public int getPosition()
1374 public Action getAction()
1379 public int getNumber()
1384 public char getGapCharacter()
1391 * Returns an iterator over the list of edit commands which traverses the list
1392 * either forwards or backwards.
1397 public Iterator<Edit> getEditIterator(boolean forwards)
1401 return getEdits().iterator();
1405 return new ReverseListIterator<Edit>(getEdits());