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,
310 int position, int number, AlignmentI al, boolean performEdit)
312 appendEdit(command, seqs, position, number, al, performEdit, null);
316 * append a new edit command with a set of alignment views that may be
327 final public void appendEdit(Action command, SequenceI[] seqs,
328 int position, int number, AlignmentI al, boolean performEdit,
331 Edit edit = new Edit(command, seqs, position, number,
332 al.getGapCharacter());
333 if (al.getHeight() == seqs.length)
336 edit.fullAlignmentHeight = true;
343 performEdit(edit, views);
348 * Overloaded method that accepts an Edit object with additional parameters.
355 final public void appendEdit(Edit edit, AlignmentI al,
356 boolean performEdit, AlignmentI[] views)
358 if (al.getHeight() == edit.seqs.length)
361 edit.fullAlignmentHeight = true;
368 performEdit(edit, views);
373 * Execute all the edit commands, starting at the given commandIndex
375 * @param commandIndex
378 public final void performEdit(int commandIndex, AlignmentI[] views)
380 ListIterator<Edit> iterator = edits.listIterator(commandIndex);
381 while (iterator.hasNext())
383 Edit edit = iterator.next();
384 performEdit(edit, views);
389 * Execute one edit command in all the specified alignment views
394 protected static void performEdit(Edit edit, AlignmentI[] views)
396 switch (edit.command)
414 // TODO:add deleteNuc for UNDO
416 // insertNuc(edits[e]);
424 final public void doCommand(AlignmentI[] views)
426 performEdit(0, views);
430 * Undo the stored list of commands, in reverse order.
433 final public void undoCommand(AlignmentI[] views)
435 ListIterator<Edit> iterator = edits.listIterator(edits.size());
436 while (iterator.hasPrevious())
438 Edit e = iterator.previous();
466 * Insert gap(s) in sequences as specified by the command, and adjust
471 final private static void insertGap(Edit command)
474 for (int s = 0; s < command.seqs.length; s++)
476 command.seqs[s].insertCharAt(command.position, command.number,
478 // System.out.println("pos: "+command.position+" number: "+command.number);
481 adjustAnnotations(command, true, false, null);
485 // final void insertNuc(Edit command)
488 // for (int s = 0; s < command.seqs.length; s++)
490 // System.out.println("pos: "+command.position+" number: "+command.number);
491 // command.seqs[s].insertCharAt(command.position, command.number,'A');
494 // adjustAnnotations(command, true, false, null);
498 * Delete gap(s) in sequences as specified by the command, and adjust
503 final static private void deleteGap(Edit command)
505 for (int s = 0; s < command.seqs.length; s++)
507 command.seqs[s].deleteChars(command.position, command.position
511 adjustAnnotations(command, false, false, null);
515 * Carry out a Cut action. The cut characters are saved in case Undo is
521 static void cut(Edit command, AlignmentI[] views)
523 boolean seqDeleted = false;
524 command.string = new char[command.seqs.length][];
526 for (int i = 0; i < command.seqs.length; i++)
528 final SequenceI sequence = command.seqs[i];
529 if (sequence.getLength() > command.position)
531 command.string[i] = sequence.getSequence(command.position,
532 command.position + command.number);
533 SequenceI oldds = sequence.getDatasetSequence();
534 if (command.oldds != null && command.oldds[i] != null)
536 // we are redoing an undone cut.
537 sequence.setDatasetSequence(null);
539 sequence.deleteChars(command.position, command.position
541 if (command.oldds != null && command.oldds[i] != null)
543 // oldds entry contains the cut dataset sequence.
544 sequence.setDatasetSequence(command.oldds[i]);
545 command.oldds[i] = oldds;
549 // modify the oldds if necessary
550 if (oldds != sequence.getDatasetSequence()
551 || sequence.getSequenceFeatures() != null)
553 if (command.oldds == null)
555 command.oldds = new SequenceI[command.seqs.length];
557 command.oldds[i] = oldds;
561 sequence.findPosition(command.position),
562 sequence.findPosition(command.position + command.number),
568 if (sequence.getLength() < 1)
570 command.al.deleteSequence(sequence);
575 adjustAnnotations(command, false, seqDeleted, views);
579 * Perform the given Paste command. This may be to add cut or copied sequences
580 * to an alignment, or to undo a 'Cut' action on a region of the alignment.
585 static void paste(Edit command, AlignmentI[] views)
589 boolean newDSWasNeeded;
590 int newstart, newend;
591 boolean seqWasDeleted = false;
592 int start = 0, end = 0;
594 for (int i = 0; i < command.seqs.length; i++)
597 newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
598 if (command.seqs[i].getLength() < 1)
600 // ie this sequence was deleted, we need to
601 // readd it to the alignment
602 if (command.alIndex[i] < command.al.getHeight())
604 List<SequenceI> sequences;
605 synchronized (sequences = command.al.getSequences())
607 if (!(command.alIndex[i] < 0))
609 sequences.add(command.alIndex[i], command.seqs[i]);
615 command.al.addSequence(command.seqs[i]);
617 seqWasDeleted = true;
619 newstart = command.seqs[i].getStart();
620 newend = command.seqs[i].getEnd();
622 tmp = new StringBuffer();
623 tmp.append(command.seqs[i].getSequence());
624 // Undo of a delete does not replace original dataset sequence on to
625 // alignment sequence.
627 if (command.string != null && command.string[i] != null)
629 if (command.position >= tmp.length())
631 // This occurs if padding is on, and residues
632 // are removed from end of alignment
633 int length = command.position - tmp.length();
636 tmp.append(command.gapChar);
640 tmp.insert(command.position, command.string[i]);
641 for (int s = 0; s < command.string[i].length; s++)
643 if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
648 start = command.seqs[i].findPosition(command.position);
649 end = command.seqs[i].findPosition(command.position
652 if (command.seqs[i].getStart() == start)
662 command.string[i] = null;
665 command.seqs[i].setSequence(tmp.toString());
666 command.seqs[i].setStart(newstart);
667 command.seqs[i].setEnd(newend);
670 if (command.seqs[i].getDatasetSequence() != null)
675 ds = command.oldds[i];
679 // make a new DS sequence
680 // use new ds mechanism here
681 ds = new Sequence(command.seqs[i].getName(),
682 jalview.analysis.AlignSeq.extractGaps(
683 jalview.util.Comparison.GapChars,
684 command.seqs[i].getSequenceAsString()),
685 command.seqs[i].getStart(), command.seqs[i].getEnd());
686 ds.setDescription(command.seqs[i].getDescription());
688 if (command.oldds == null)
690 command.oldds = new SequenceI[command.seqs.length];
692 command.oldds[i] = command.seqs[i].getDatasetSequence();
693 command.seqs[i].setDatasetSequence(ds);
695 adjustFeatures(command, i, start, end, true);
698 adjustAnnotations(command, true, seqWasDeleted, views);
700 command.string = null;
703 static void replace(Edit command)
707 int start = command.position;
708 int end = command.number;
709 // TODO TUTORIAL - Fix for replacement with different length of sequence (or
711 // TODO Jalview 2.4 bugfix change to an aggregate command - original
712 // sequence string is cut, new string is pasted in.
713 command.number = start + command.string[0].length;
714 for (int i = 0; i < command.seqs.length; i++)
716 boolean newDSWasNeeded = command.oldds != null
717 && command.oldds[i] != null;
720 * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
721 * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
722 * viewport.alignment));
726 * then addHistoryItem(new EditCommand( "Add sequences",
727 * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
730 oldstring = command.seqs[i].getSequenceAsString();
731 tmp = new StringBuffer(oldstring.substring(0, start));
732 tmp.append(command.string[i]);
733 String nogaprep = jalview.analysis.AlignSeq.extractGaps(
734 jalview.util.Comparison.GapChars, new String(
736 int ipos = command.seqs[i].findPosition(start)
737 - command.seqs[i].getStart();
738 tmp.append(oldstring.substring(end));
739 command.seqs[i].setSequence(tmp.toString());
740 command.string[i] = oldstring.substring(start, end).toCharArray();
741 String nogapold = jalview.analysis.AlignSeq.extractGaps(
742 jalview.util.Comparison.GapChars, new String(
744 if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
748 SequenceI oldds = command.seqs[i].getDatasetSequence();
749 command.seqs[i].setDatasetSequence(command.oldds[i]);
750 command.oldds[i] = oldds;
754 if (command.oldds == null)
756 command.oldds = new SequenceI[command.seqs.length];
758 command.oldds[i] = command.seqs[i].getDatasetSequence();
759 SequenceI newds = new Sequence(
760 command.seqs[i].getDatasetSequence());
761 String fullseq, osp = newds.getSequenceAsString();
762 fullseq = osp.substring(0, ipos) + nogaprep
763 + osp.substring(ipos + nogaprep.length());
764 newds.setSequence(fullseq.toUpperCase());
765 // TODO: JAL-1131 ensure newly created dataset sequence is added to
767 // dataset sequences associated with the alignment.
768 // TODO: JAL-1131 fix up any annotation associated with new dataset
769 // sequence to ensure that original sequence/annotation relationships
771 command.seqs[i].setDatasetSequence(newds);
780 final static void adjustAnnotations(Edit command, boolean insert,
781 boolean modifyVisibility, AlignmentI[] views)
783 AlignmentAnnotation[] annotations = null;
785 if (modifyVisibility && !insert)
787 // only occurs if a sequence was added or deleted.
788 command.deletedAnnotationRows = new Hashtable<SequenceI, AlignmentAnnotation[]>();
790 if (command.fullAlignmentHeight)
792 annotations = command.al.getAlignmentAnnotation();
797 AlignmentAnnotation[] tmp;
798 for (int s = 0; s < command.seqs.length; s++)
800 if (modifyVisibility)
802 // Rows are only removed or added to sequence object.
806 tmp = command.seqs[s].getAnnotation();
809 int alen = tmp.length;
810 for (int aa = 0; aa < tmp.length; aa++)
812 if (!command.al.deleteAnnotation(tmp[aa]))
814 // strip out annotation not in the current al (will be put
815 // back on insert in all views)
820 command.seqs[s].setAlignmentAnnotation(null);
821 if (alen != tmp.length)
823 // save the non-null annotation references only
824 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
825 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
829 saved[aapos++] = tmp[aa];
834 command.deletedAnnotationRows.put(command.seqs[s], saved);
835 // and then remove any annotation in the other views
836 for (int alview = 0; views != null && alview < views.length; alview++)
838 if (views[alview] != command.al)
840 AlignmentAnnotation[] toremove = views[alview]
841 .getAlignmentAnnotation();
842 if (toremove == null || toremove.length == 0)
846 // remove any alignment annotation on this sequence that's
847 // on that alignment view.
848 for (int aa = 0; aa < toremove.length; aa++)
850 if (toremove[aa].sequenceRef == command.seqs[s])
852 views[alview].deleteAnnotation(toremove[aa]);
860 // save all the annotation
861 command.deletedAnnotationRows.put(command.seqs[s], tmp);
868 if (command.deletedAnnotationRows != null
869 && command.deletedAnnotationRows
870 .containsKey(command.seqs[s]))
872 AlignmentAnnotation[] revealed = command.deletedAnnotationRows
873 .get(command.seqs[s]);
874 command.seqs[s].setAlignmentAnnotation(revealed);
875 if (revealed != null)
877 for (int aa = 0; aa < revealed.length; aa++)
879 // iterate through al adding original annotation
880 command.al.addAnnotation(revealed[aa]);
882 for (int aa = 0; aa < revealed.length; aa++)
884 command.al.setAnnotationIndex(revealed[aa], aa);
886 // and then duplicate added annotation on every other alignment
888 for (int vnum = 0; views != null && vnum < views.length; vnum++)
890 if (views[vnum] != command.al)
892 int avwidth = views[vnum].getWidth() + 1;
893 // duplicate in this view
894 for (int a = 0; a < revealed.length; a++)
896 AlignmentAnnotation newann = new AlignmentAnnotation(
898 command.seqs[s].addAlignmentAnnotation(newann);
899 newann.padAnnotation(avwidth);
900 views[vnum].addAnnotation(newann);
901 views[vnum].setAnnotationIndex(newann, a);
911 if (command.seqs[s].getAnnotation() == null)
918 annotations = command.seqs[s].getAnnotation();
922 tmp = new AlignmentAnnotation[aSize
923 + command.seqs[s].getAnnotation().length];
925 System.arraycopy(annotations, 0, tmp, 0, aSize);
927 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
928 command.seqs[s].getAnnotation().length);
932 aSize = annotations.length;
936 if (annotations == null)
943 command.deletedAnnotations = new Hashtable<String, Annotation[]>();
948 for (int a = 0; a < annotations.length; a++)
950 if (annotations[a].autoCalculated
951 || annotations[a].annotations == null)
958 aSize = annotations[a].annotations.length;
961 temp = new Annotation[aSize + command.number];
962 if (annotations[a].padGaps)
964 for (int aa = 0; aa < temp.length; aa++)
966 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
972 if (command.position < aSize)
974 if (command.position + command.number >= aSize)
980 tSize = aSize - command.number;
992 temp = new Annotation[tSize];
997 if (command.position < annotations[a].annotations.length)
999 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1002 if (command.deletedAnnotations != null
1003 && command.deletedAnnotations
1004 .containsKey(annotations[a].annotationId))
1006 Annotation[] restore = command.deletedAnnotations
1007 .get(annotations[a].annotationId);
1009 System.arraycopy(restore, 0, temp, command.position,
1014 System.arraycopy(annotations[a].annotations, command.position,
1015 temp, command.position + command.number, aSize
1016 - command.position);
1020 if (command.deletedAnnotations != null
1021 && command.deletedAnnotations
1022 .containsKey(annotations[a].annotationId))
1024 Annotation[] restore = command.deletedAnnotations
1025 .get(annotations[a].annotationId);
1027 temp = new Annotation[annotations[a].annotations.length
1029 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1030 annotations[a].annotations.length);
1031 System.arraycopy(restore, 0, temp,
1032 annotations[a].annotations.length, restore.length);
1036 temp = annotations[a].annotations;
1042 if (tSize != aSize || command.position < 2)
1044 int copylen = Math.min(command.position,
1045 annotations[a].annotations.length);
1048 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1049 copylen); // command.position);
1052 Annotation[] deleted = new Annotation[command.number];
1053 if (copylen >= command.position)
1055 copylen = Math.min(command.number,
1056 annotations[a].annotations.length - command.position);
1059 System.arraycopy(annotations[a].annotations,
1060 command.position, deleted, 0, copylen); // command.number);
1064 command.deletedAnnotations.put(annotations[a].annotationId,
1066 if (annotations[a].annotations.length > command.position
1069 System.arraycopy(annotations[a].annotations, command.position
1070 + command.number, temp, command.position,
1071 annotations[a].annotations.length - command.position
1072 - command.number); // aSize
1077 int dSize = aSize - command.position;
1081 Annotation[] deleted = new Annotation[command.number];
1082 System.arraycopy(annotations[a].annotations, command.position,
1085 command.deletedAnnotations.put(annotations[a].annotationId,
1088 tSize = Math.min(annotations[a].annotations.length,
1090 temp = new Annotation[tSize];
1091 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
1095 temp = annotations[a].annotations;
1100 annotations[a].annotations = temp;
1104 final static void adjustFeatures(Edit command, int index, int i, int j,
1107 SequenceI seq = command.seqs[index];
1108 SequenceI sequence = seq.getDatasetSequence();
1109 if (sequence == null)
1116 if (command.editedFeatures != null
1117 && command.editedFeatures.containsKey(seq))
1119 sequence.setSequenceFeatures(command.editedFeatures.get(seq));
1125 SequenceFeature[] sf = sequence.getSequenceFeatures();
1132 SequenceFeature[] oldsf = new SequenceFeature[sf.length];
1136 for (int s = 0; s < sf.length; s++)
1138 SequenceFeature copy = new SequenceFeature(sf[s]);
1142 if (sf[s].getEnd() < i)
1147 if (sf[s].getBegin() > j)
1149 sf[s].setBegin(copy.getBegin() - cSize);
1150 sf[s].setEnd(copy.getEnd() - cSize);
1154 if (sf[s].getBegin() >= i)
1159 if (sf[s].getEnd() < j)
1161 sf[s].setEnd(j - 1);
1164 sf[s].setEnd(sf[s].getEnd() - (cSize));
1166 if (sf[s].getBegin() > sf[s].getEnd())
1168 sequence.deleteFeature(sf[s]);
1172 if (command.editedFeatures == null)
1174 command.editedFeatures = new Hashtable<SequenceI, SequenceFeature[]>();
1177 command.editedFeatures.put(seq, oldsf);
1182 * Returns the list of edit commands wrapped by this object.
1186 public List<Edit> getEdits()
1192 * Returns a map whose keys are the dataset sequences, and values their
1193 * aligned sequences before the command edit list was applied. The aligned
1194 * sequences are copies, which may be updated without affecting the originals.
1196 * The command holds references to the aligned sequences (after editing). If
1197 * the command is an 'undo',then the prior state is simply the aligned state.
1198 * Otherwise, we have to derive the prior state by working backwards through
1199 * the edit list to infer the aligned sequences before editing.
1201 * Note: an alternative solution would be to cache the 'before' state of each
1202 * edit, but this would be expensive in space in the common case that the
1203 * original is never needed (edits are not mirrored).
1206 * @throws IllegalStateException
1207 * on detecting an edit command of a type that can't be unwound
1209 public Map<SequenceI, SequenceI> priorState(boolean forUndo)
1211 Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
1212 if (getEdits() == null)
1218 for (Edit e : getEdits())
1220 for (SequenceI seq : e.getSequences())
1222 SequenceI ds = seq.getDatasetSequence();
1223 // SequenceI preEdit = result.get(ds);
1224 if (!result.containsKey(ds))
1227 * copy sequence including start/end (but don't use copy constructor
1228 * as we don't need annotations)
1230 SequenceI preEdit = new Sequence("", seq.getSequenceAsString(),
1231 seq.getStart(), seq.getEnd());
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> editList = new ReverseListIterator<Edit>(getEdits());
1245 while (editList.hasNext())
1247 Edit oldEdit = editList.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 seq.getStart(), seq.getEnd());
1260 preEdit.setDatasetSequence(ds);
1261 result.put(ds, preEdit);
1264 * 'Undo' this edit action on the sequence (updating the value in the
1269 if (action == Action.DELETE_GAP)
1271 preEdit.setSequence(new String(StringUtils.insertCharAt(
1272 preEdit.getSequence(), position, number, gap)));
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());