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()
81 // public Action getUndoAction()
90 // public Action getUndoAction()
99 // public Action getUndoAction()
108 // public Action getUndoAction()
117 // public Action getUndoAction()
123 public Action getUndoAction() {
142 private List<Edit> edits = new ArrayList<Edit>();
150 public EditCommand(String description)
152 this.description = description;
155 public EditCommand(String description, Action command, SequenceI[] seqs,
156 int position, int number, AlignmentI al)
158 this.description = description;
159 if (command == Action.CUT || command == Action.PASTE)
161 setEdit(new Edit(command, seqs, position, number, al));
164 performEdit(0, null);
167 public EditCommand(String description, Action command, String replace,
168 SequenceI[] seqs, int position, int number, AlignmentI al)
170 this.description = description;
171 if (command == Action.REPLACE)
173 setEdit(new Edit(command, seqs, position, number, al, replace));
176 performEdit(0, null);
180 * Set the list of edits to the specified item (only).
184 protected void setEdit(Edit e)
191 * Add the given edit command to the stored list of commands. If simply
192 * expanding the range of the last command added, then modify it instead of
193 * adding a new command.
197 public void addEdit(Edit e)
199 if (!expandEdit(edits, e))
206 * Returns true if the new edit is incorporated by updating (expanding the
207 * range of) the last edit on the list, else false. We can 'expand' the last
208 * edit if the new one is the same action, on the same sequences, and acts on
209 * a contiguous range. This is the case where a mouse drag generates a series
210 * of contiguous gap insertions or deletions.
216 protected static boolean expandEdit(List<Edit> edits, Edit e)
218 if (edits == null || edits.isEmpty())
222 Edit lastEdit = edits.get(edits.size() - 1);
223 Action action = e.command;
224 if (lastEdit.command != action)
230 * Both commands must act on the same sequences - compare the underlying
231 * dataset sequences, rather than the aligned sequences, which change as
234 if (lastEdit.seqs.length != e.seqs.length)
238 for (int i = 0; i < e.seqs.length; i++)
240 if (lastEdit.seqs[i].getDatasetSequence() != e.seqs[i]
241 .getDatasetSequence())
248 * Check a contiguous edit; either
250 * <li>a new Insert <n> positions to the right of the last <insert n>, or</li>
251 * <li>a new Delete <n> gaps which is <n> positions to the left of the last
255 boolean contiguous = (action == Action.INSERT_GAP && e.position == lastEdit.position
257 || (action == Action.DELETE_GAP && e.position + e.number == lastEdit.position);
261 * We are just expanding the range of the last edit. For delete gap, also
262 * moving the start position left.
264 lastEdit.number += e.number;
265 lastEdit.seqs = e.seqs;
266 if (action == Action.DELETE_GAP)
276 * Clear the list of stored edit commands.
279 protected void clearEdits()
285 * Returns the i'th stored Edit command.
290 protected Edit getEdit(int i)
292 if (i >= 0 && i < edits.size())
300 final public String getDescription()
312 * Return the alignment for the first edit (or null if no edit).
316 final public AlignmentI getAlignment()
318 return (edits.isEmpty() ? null : edits.get(0).al);
322 * append a new editCommand Note. this shouldn't be called if the edit is an
323 * operation affects more alignment objects than the one referenced in al (for
324 * example, cut or pasting whole sequences). Use the form with an additional
325 * AlignmentI[] views parameter.
334 final public void appendEdit(Action command, SequenceI[] seqs,
336 int number, AlignmentI al, boolean performEdit)
338 appendEdit(command, seqs, position, number, al, performEdit, null);
342 * append a new edit command with a set of alignment views that may be
353 final public void appendEdit(Action command, SequenceI[] seqs,
355 int number, AlignmentI al, boolean performEdit, AlignmentI[] views)
357 Edit edit = new Edit(command, seqs, position, number,
358 al.getGapCharacter());
359 if (al.getHeight() == seqs.length)
362 edit.fullAlignmentHeight = true;
369 performEdit(edit, views);
374 * Overloaded method that accepts an Edit object with additional parameters.
381 final public void appendEdit(Edit edit, AlignmentI al,
382 boolean performEdit, AlignmentI[] views)
384 if (al.getHeight() == edit.seqs.length)
387 edit.fullAlignmentHeight = true;
394 performEdit(edit, views);
399 * Execute all the edit commands, starting at the given commandIndex
401 * @param commandIndex
404 public final void performEdit(int commandIndex, AlignmentI[] views)
406 ListIterator<Edit> iterator = edits.listIterator(commandIndex);
407 while (iterator.hasNext())
409 Edit edit = iterator.next();
410 performEdit(edit, views);
415 * Execute one edit command in all the specified alignment views
420 protected static void performEdit(Edit edit, AlignmentI[] views)
422 switch (edit.command)
440 // TODO:add deleteNuc for UNDO
442 // insertNuc(edits[e]);
450 final public void doCommand(AlignmentI[] views)
452 performEdit(0, views);
456 * Undo the stored list of commands, in reverse order.
459 final public void undoCommand(AlignmentI[] views)
461 ListIterator<Edit> iterator = edits.listIterator(edits.size());
462 while (iterator.hasPrevious())
464 Edit e = iterator.previous();
492 * Insert gap(s) in sequences as specified by the command, and adjust
497 final private static void insertGap(Edit command)
500 for (int s = 0; s < command.seqs.length; s++)
502 command.seqs[s].insertCharAt(command.position,
503 command.number, command.gapChar);
504 // System.out.println("pos: "+command.position+" number: "+command.number);
507 adjustAnnotations(command, true, false, null);
511 // final void insertNuc(Edit command)
514 // for (int s = 0; s < command.seqs.length; s++)
516 // System.out.println("pos: "+command.position+" number: "+command.number);
517 // command.seqs[s].insertCharAt(command.position, command.number,'A');
520 // adjustAnnotations(command, true, false, null);
524 * Delete gap(s) in sequences as specified by the command, and adjust
529 final static private void deleteGap(Edit command)
531 for (int s = 0; s < command.seqs.length; s++)
533 command.seqs[s].deleteChars(command.position, command.position
537 adjustAnnotations(command, false, false, null);
541 * Carry out a Cut action. The cut characters are saved in case Undo is
547 static void cut(Edit command, AlignmentI[] views)
549 boolean seqDeleted = false;
550 command.string = new char[command.seqs.length][];
552 for (int i = 0; i < command.seqs.length; i++)
554 final SequenceI sequence = command.seqs[i];
555 if (sequence.getLength() > command.position)
557 command.string[i] = sequence.getSequence(command.position,
558 command.position + command.number);
559 SequenceI oldds = sequence.getDatasetSequence();
560 if (command.oldds != null && command.oldds[i] != null)
562 // we are redoing an undone cut.
563 sequence.setDatasetSequence(null);
565 sequence.deleteChars(command.position, command.position
567 if (command.oldds != null && command.oldds[i] != null)
569 // oldds entry contains the cut dataset sequence.
570 sequence.setDatasetSequence(command.oldds[i]);
571 command.oldds[i] = oldds;
575 // modify the oldds if necessary
576 if (oldds != sequence.getDatasetSequence()
577 || sequence.getSequenceFeatures() != null)
579 if (command.oldds == null)
581 command.oldds = new SequenceI[command.seqs.length];
583 command.oldds[i] = oldds;
587 sequence.findPosition(command.position),
588 sequence.findPosition(command.position
589 + command.number), false);
594 if (sequence.getLength() < 1)
596 command.al.deleteSequence(sequence);
601 adjustAnnotations(command, false, seqDeleted, views);
605 * Perform the given Paste command. This may be to add cut or copied sequences
606 * to an alignment, or to undo a 'Cut' action on a region of the alignment.
611 static void paste(Edit command, AlignmentI[] views)
615 boolean newDSWasNeeded;
616 int newstart, newend;
617 boolean seqWasDeleted = false;
618 int start = 0, end = 0;
620 for (int i = 0; i < command.seqs.length; i++)
623 newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
624 if (command.seqs[i].getLength() < 1)
626 // ie this sequence was deleted, we need to
627 // readd it to the alignment
628 if (command.alIndex[i] < command.al.getHeight())
630 List<SequenceI> sequences;
631 synchronized (sequences = command.al.getSequences())
633 if (!(command.alIndex[i] < 0))
635 sequences.add(command.alIndex[i], command.seqs[i]);
641 command.al.addSequence(command.seqs[i]);
643 seqWasDeleted = true;
645 newstart = command.seqs[i].getStart();
646 newend = command.seqs[i].getEnd();
648 tmp = new StringBuffer();
649 tmp.append(command.seqs[i].getSequence());
650 // Undo of a delete does not replace original dataset sequence on to
651 // alignment sequence.
653 if (command.string != null && command.string[i] != null)
655 if (command.position >= tmp.length())
657 // This occurs if padding is on, and residues
658 // are removed from end of alignment
659 int length = command.position - tmp.length();
662 tmp.append(command.gapChar);
666 tmp.insert(command.position, command.string[i]);
667 for (int s = 0; s < command.string[i].length; s++)
669 if (ResidueProperties.aaIndex[command.string[i][s]] != 23)
674 start = command.seqs[i].findPosition(command.position);
675 end = command.seqs[i].findPosition(command.position
678 if (command.seqs[i].getStart() == start)
688 command.string[i] = null;
691 command.seqs[i].setSequence(tmp.toString());
692 command.seqs[i].setStart(newstart);
693 command.seqs[i].setEnd(newend);
696 if (command.seqs[i].getDatasetSequence() != null)
701 ds = command.oldds[i];
705 // make a new DS sequence
706 // use new ds mechanism here
707 ds = new Sequence(command.seqs[i].getName(),
708 AlignSeq.extractGaps(
710 command.seqs[i].getSequenceAsString()),
711 command.seqs[i].getStart(), command.seqs[i].getEnd());
712 ds.setDescription(command.seqs[i].getDescription());
714 if (command.oldds == null)
716 command.oldds = new SequenceI[command.seqs.length];
718 command.oldds[i] = command.seqs[i].getDatasetSequence();
719 command.seqs[i].setDatasetSequence(ds);
721 adjustFeatures(command, i, start, end, true);
724 adjustAnnotations(command, true, seqWasDeleted, views);
726 command.string = null;
729 static void replace(Edit command)
733 int start = command.position;
734 int end = command.number;
735 // TODO TUTORIAL - Fix for replacement with different length of sequence (or
737 // TODO Jalview 2.4 bugfix change to an aggregate command - original
738 // sequence string is cut, new string is pasted in.
739 command.number = start + command.string[0].length;
740 for (int i = 0; i < command.seqs.length; i++)
742 boolean newDSWasNeeded = command.oldds != null
743 && command.oldds[i] != null;
746 * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
747 * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
748 * viewport.alignment));
752 * then addHistoryItem(new EditCommand( "Add sequences",
753 * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
756 oldstring = command.seqs[i].getSequenceAsString();
757 tmp = new StringBuffer(oldstring.substring(0, start));
758 tmp.append(command.string[i]);
759 String nogaprep = AlignSeq.extractGaps(
760 Comparison.GapChars, new String(
762 int ipos = command.seqs[i].findPosition(start)
763 - command.seqs[i].getStart();
764 tmp.append(oldstring.substring(end));
765 command.seqs[i].setSequence(tmp.toString());
766 command.string[i] = oldstring.substring(start, end).toCharArray();
767 String nogapold = AlignSeq.extractGaps(
768 Comparison.GapChars, new String(
770 if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
774 SequenceI oldds = command.seqs[i].getDatasetSequence();
775 command.seqs[i].setDatasetSequence(command.oldds[i]);
776 command.oldds[i] = oldds;
780 if (command.oldds == null)
782 command.oldds = new SequenceI[command.seqs.length];
784 command.oldds[i] = command.seqs[i].getDatasetSequence();
785 SequenceI newds = new Sequence(
786 command.seqs[i].getDatasetSequence());
787 String fullseq, osp = newds.getSequenceAsString();
788 fullseq = osp.substring(0, ipos) + nogaprep
789 + osp.substring(ipos + nogaprep.length());
790 newds.setSequence(fullseq.toUpperCase());
791 // TODO: JAL-1131 ensure newly created dataset sequence is added to
793 // dataset sequences associated with the alignment.
794 // TODO: JAL-1131 fix up any annotation associated with new dataset
795 // sequence to ensure that original sequence/annotation relationships
797 command.seqs[i].setDatasetSequence(newds);
806 final static void adjustAnnotations(Edit command, boolean insert,
807 boolean modifyVisibility, AlignmentI[] views)
809 AlignmentAnnotation[] annotations = null;
811 if (modifyVisibility && !insert)
813 // only occurs if a sequence was added or deleted.
814 command.deletedAnnotationRows = new Hashtable<SequenceI, AlignmentAnnotation[]>();
816 if (command.fullAlignmentHeight)
818 annotations = command.al.getAlignmentAnnotation();
823 AlignmentAnnotation[] tmp;
824 for (int s = 0; s < command.seqs.length; s++)
826 if (modifyVisibility)
828 // Rows are only removed or added to sequence object.
832 tmp = command.seqs[s].getAnnotation();
835 int alen = tmp.length;
836 for (int aa = 0; aa < tmp.length; aa++)
838 if (!command.al.deleteAnnotation(tmp[aa]))
840 // strip out annotation not in the current al (will be put
841 // back on insert in all views)
846 command.seqs[s].setAlignmentAnnotation(null);
847 if (alen != tmp.length)
849 // save the non-null annotation references only
850 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
851 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
855 saved[aapos++] = tmp[aa];
860 command.deletedAnnotationRows.put(command.seqs[s], saved);
861 // and then remove any annotation in the other views
862 for (int alview = 0; views != null && alview < views.length; alview++)
864 if (views[alview] != command.al)
866 AlignmentAnnotation[] toremove = views[alview]
867 .getAlignmentAnnotation();
868 if (toremove == null || toremove.length == 0)
872 // remove any alignment annotation on this sequence that's
873 // on that alignment view.
874 for (int aa = 0; aa < toremove.length; aa++)
876 if (toremove[aa].sequenceRef == command.seqs[s])
878 views[alview].deleteAnnotation(toremove[aa]);
886 // save all the annotation
887 command.deletedAnnotationRows.put(command.seqs[s], tmp);
894 if (command.deletedAnnotationRows != null
895 && command.deletedAnnotationRows
896 .containsKey(command.seqs[s]))
898 AlignmentAnnotation[] revealed = command.deletedAnnotationRows
899 .get(command.seqs[s]);
900 command.seqs[s].setAlignmentAnnotation(revealed);
901 if (revealed != null)
903 for (int aa = 0; aa < revealed.length; aa++)
905 // iterate through al adding original annotation
906 command.al.addAnnotation(revealed[aa]);
908 for (int aa = 0; aa < revealed.length; aa++)
910 command.al.setAnnotationIndex(revealed[aa], aa);
912 // and then duplicate added annotation on every other alignment
914 for (int vnum = 0; views != null && vnum < views.length; vnum++)
916 if (views[vnum] != command.al)
918 int avwidth = views[vnum].getWidth() + 1;
919 // duplicate in this view
920 for (int a = 0; a < revealed.length; a++)
922 AlignmentAnnotation newann = new AlignmentAnnotation(
924 command.seqs[s].addAlignmentAnnotation(newann);
925 newann.padAnnotation(avwidth);
926 views[vnum].addAnnotation(newann);
927 views[vnum].setAnnotationIndex(newann, a);
937 if (command.seqs[s].getAnnotation() == null)
944 annotations = command.seqs[s].getAnnotation();
948 tmp = new AlignmentAnnotation[aSize
949 + command.seqs[s].getAnnotation().length];
951 System.arraycopy(annotations, 0, tmp, 0, aSize);
953 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
954 command.seqs[s].getAnnotation().length);
958 aSize = annotations.length;
962 if (annotations == null)
969 command.deletedAnnotations = new Hashtable<String, Annotation[]>();
974 for (int a = 0; a < annotations.length; a++)
976 if (annotations[a].autoCalculated
977 || annotations[a].annotations == null)
984 aSize = annotations[a].annotations.length;
987 temp = new Annotation[aSize + command.number];
988 if (annotations[a].padGaps)
990 for (int aa = 0; aa < temp.length; aa++)
992 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
998 if (command.position < aSize)
1000 if (command.position + command.number >= aSize)
1006 tSize = aSize - command.number;
1018 temp = new Annotation[tSize];
1023 if (command.position < annotations[a].annotations.length)
1025 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1028 if (command.deletedAnnotations != null
1029 && command.deletedAnnotations
1030 .containsKey(annotations[a].annotationId))
1032 Annotation[] restore = command.deletedAnnotations
1033 .get(annotations[a].annotationId);
1035 System.arraycopy(restore, 0, temp, command.position,
1040 System.arraycopy(annotations[a].annotations, command.position,
1041 temp, command.position + command.number, aSize
1042 - command.position);
1046 if (command.deletedAnnotations != null
1047 && command.deletedAnnotations
1048 .containsKey(annotations[a].annotationId))
1050 Annotation[] restore = command.deletedAnnotations
1051 .get(annotations[a].annotationId);
1053 temp = new Annotation[annotations[a].annotations.length
1055 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1056 annotations[a].annotations.length);
1057 System.arraycopy(restore, 0, temp,
1058 annotations[a].annotations.length, restore.length);
1062 temp = annotations[a].annotations;
1068 if (tSize != aSize || command.position < 2)
1070 int copylen = Math.min(command.position,
1071 annotations[a].annotations.length);
1074 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1075 copylen); // command.position);
1078 Annotation[] deleted = new Annotation[command.number];
1079 if (copylen >= command.position)
1081 copylen = Math.min(command.number,
1082 annotations[a].annotations.length - command.position);
1085 System.arraycopy(annotations[a].annotations,
1086 command.position, deleted, 0, copylen); // command.number);
1090 command.deletedAnnotations.put(annotations[a].annotationId,
1092 if (annotations[a].annotations.length > command.position
1095 System.arraycopy(annotations[a].annotations, command.position
1096 + command.number, temp, command.position,
1097 annotations[a].annotations.length - command.position
1098 - command.number); // aSize
1103 int dSize = aSize - command.position;
1107 Annotation[] deleted = new Annotation[command.number];
1108 System.arraycopy(annotations[a].annotations, command.position,
1111 command.deletedAnnotations.put(annotations[a].annotationId,
1114 tSize = Math.min(annotations[a].annotations.length,
1116 temp = new Annotation[tSize];
1117 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
1121 temp = annotations[a].annotations;
1126 annotations[a].annotations = temp;
1130 final static void adjustFeatures(Edit command, int index, int i, int j,
1133 SequenceI seq = command.seqs[index];
1134 SequenceI sequence = seq.getDatasetSequence();
1135 if (sequence == null)
1142 if (command.editedFeatures != null
1143 && command.editedFeatures.containsKey(seq))
1145 sequence.setSequenceFeatures(command.editedFeatures
1152 SequenceFeature[] sf = sequence.getSequenceFeatures();
1159 SequenceFeature[] oldsf = new SequenceFeature[sf.length];
1163 for (int s = 0; s < sf.length; s++)
1165 SequenceFeature copy = new SequenceFeature(sf[s]);
1169 if (sf[s].getEnd() < i)
1174 if (sf[s].getBegin() > j)
1176 sf[s].setBegin(copy.getBegin() - cSize);
1177 sf[s].setEnd(copy.getEnd() - cSize);
1181 if (sf[s].getBegin() >= i)
1186 if (sf[s].getEnd() < j)
1188 sf[s].setEnd(j - 1);
1191 sf[s].setEnd(sf[s].getEnd() - (cSize));
1193 if (sf[s].getBegin() > sf[s].getEnd())
1195 sequence.deleteFeature(sf[s]);
1199 if (command.editedFeatures == null)
1201 command.editedFeatures = new Hashtable<SequenceI, SequenceFeature[]>();
1204 command.editedFeatures.put(seq, oldsf);
1209 * Returns the list of edit commands wrapped by this object.
1213 public List<Edit> getEdits()
1219 * Returns a map whose keys are the dataset sequences, and values their
1220 * aligned sequences before the command edit list was applied. The aligned
1221 * sequences are copies, which may be updated without affecting the originals.
1223 * The command holds references to the aligned sequences (after editing). If
1224 * the command is an 'undo',then the prior state is simply the aligned state.
1225 * Otherwise, we have to derive the prior state by working backwards through
1226 * the edit list to infer the aligned sequences before editing.
1228 * Note: an alternative solution would be to cache the 'before' state of each
1229 * edit, but this would be expensive in space in the common case that the
1230 * original is never needed (edits are not mirrored).
1233 * @throws IllegalStateException
1234 * on detecting an edit command of a type that can't be unwound
1236 public Map<SequenceI, SequenceI> priorState(boolean forUndo)
1238 Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
1239 if (getEdits() == null)
1245 for (Edit e : getEdits())
1247 for (SequenceI seq : e.getSequences())
1249 SequenceI ds = seq.getDatasetSequence();
1250 SequenceI preEdit = result.get(ds);
1251 if (preEdit == null)
1253 preEdit = new Sequence("", seq.getSequenceAsString());
1254 preEdit.setDatasetSequence(ds);
1255 result.put(ds, preEdit);
1263 * Work backwards through the edit list, deriving the sequences before each
1264 * was applied. The final result is the sequence set before any edits.
1266 Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
1267 while (edits.hasNext())
1269 Edit oldEdit = edits.next();
1270 Action action = oldEdit.getAction();
1271 int position = oldEdit.getPosition();
1272 int number = oldEdit.getNumber();
1273 final char gap = oldEdit.getGapCharacter();
1274 for (SequenceI seq : oldEdit.getSequences())
1276 SequenceI ds = seq.getDatasetSequence();
1277 SequenceI preEdit = result.get(ds);
1278 if (preEdit == null)
1280 preEdit = new Sequence("", seq.getSequenceAsString());
1281 preEdit.setDatasetSequence(ds);
1282 result.put(ds, preEdit);
1285 * 'Undo' this edit action on the sequence (updating the value in the
1290 if (action == Action.DELETE_GAP)
1292 preEdit.setSequence(new String(StringUtils.insertCharAt(
1293 preEdit.getSequence(), position,
1296 else if (action == Action.INSERT_GAP)
1298 preEdit.setSequence(new String(StringUtils.deleteChars(
1299 preEdit.getSequence(), position, position + number)));
1303 System.err.println("Can't undo edit action " + action);
1304 // throw new IllegalStateException("Can't undo edit action " +
1315 public SequenceI[] oldds;
1317 boolean fullAlignmentHeight = false;
1319 Hashtable<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
1321 Hashtable<String, Annotation[]> deletedAnnotations;
1323 Hashtable<SequenceI, SequenceFeature[]> editedFeatures;
1335 int position, number;
1339 public Edit(Action command, SequenceI[] seqs, int position, int number,
1342 this.command = command;
1344 this.position = position;
1345 this.number = number;
1346 this.gapChar = gapChar;
1349 Edit(Action command, SequenceI[] seqs, int position, int number,
1352 this.gapChar = al.getGapCharacter();
1353 this.command = command;
1355 this.position = position;
1356 this.number = number;
1359 alIndex = new int[seqs.length];
1360 for (int i = 0; i < seqs.length; i++)
1362 alIndex[i] = al.findIndex(seqs[i]);
1365 fullAlignmentHeight = (al.getHeight() == seqs.length);
1368 Edit(Action command, SequenceI[] seqs, int position, int number,
1369 AlignmentI al, String replace)
1371 this.command = command;
1373 this.position = position;
1374 this.number = number;
1376 this.gapChar = al.getGapCharacter();
1377 string = new char[seqs.length][];
1378 for (int i = 0; i < seqs.length; i++)
1380 string[i] = replace.toCharArray();
1383 fullAlignmentHeight = (al.getHeight() == seqs.length);
1386 public SequenceI[] getSequences()
1391 public int getPosition()
1396 public Action getAction()
1401 public int getNumber()
1406 public char getGapCharacter()
1413 * Returns an iterator over the list of edit commands which traverses the list
1414 * either forwards or backwards.
1419 public Iterator<Edit> getEditIterator(boolean forwards)
1423 return getEdits().iterator();
1427 return new ReverseListIterator<Edit>(getEdits());