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 desc)
127 this.description = desc;
130 public EditCommand(String desc, Action command, SequenceI[] seqs,
131 int position, int number, AlignmentI al)
133 this.description = desc;
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 desc, Action command, String replace,
143 SequenceI[] seqs, int position, int number, AlignmentI al)
145 this.description = desc;
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>,
227 * <li>a new Delete <n> gaps which is <n> positions to the left of the last
231 boolean contiguous = (action == Action.INSERT_GAP
232 && e.position == lastEdit.position + lastEdit.number)
233 || (action == Action.DELETE_GAP
234 && e.position + e.number == lastEdit.position);
238 * We are just expanding the range of the last edit. For delete gap, also
239 * moving the start position left.
241 lastEdit.number += e.number;
242 lastEdit.seqs = e.seqs;
243 if (action == Action.DELETE_GAP)
253 * Clear the list of stored edit commands.
256 protected void clearEdits()
262 * Returns the i'th stored Edit command.
267 protected Edit getEdit(int i)
269 if (i >= 0 && i < edits.size())
277 final public String getDescription()
289 * Return the alignment for the first edit (or null if no edit).
293 final public AlignmentI getAlignment()
295 return (edits.isEmpty() ? null : edits.get(0).al);
299 * append a new editCommand Note. this shouldn't be called if the edit is an
300 * operation affects more alignment objects than the one referenced in al (for
301 * example, cut or pasting whole sequences). Use the form with an additional
302 * AlignmentI[] views parameter.
311 final public void appendEdit(Action command, SequenceI[] seqs,
312 int position, int number, AlignmentI al, boolean performEdit)
314 appendEdit(command, seqs, position, number, al, performEdit, null);
318 * append a new edit command with a set of alignment views that may be
329 final public void appendEdit(Action command, SequenceI[] seqs,
330 int position, int number, AlignmentI al, boolean performEdit,
333 Edit edit = new Edit(command, seqs, position, number,
334 al.getGapCharacter());
335 if (al.getHeight() == seqs.length)
338 edit.fullAlignmentHeight = true;
345 performEdit(edit, views);
350 * Overloaded method that accepts an Edit object with additional parameters.
357 final public void appendEdit(Edit edit, AlignmentI al,
358 boolean performEdit, AlignmentI[] views)
360 if (al.getHeight() == edit.seqs.length)
363 edit.fullAlignmentHeight = true;
370 performEdit(edit, views);
375 * Execute all the edit commands, starting at the given commandIndex
377 * @param commandIndex
380 public final void performEdit(int commandIndex, AlignmentI[] views)
382 ListIterator<Edit> iterator = edits.listIterator(commandIndex);
383 while (iterator.hasNext())
385 Edit edit = iterator.next();
386 performEdit(edit, views);
391 * Execute one edit command in all the specified alignment views
396 protected static void performEdit(Edit edit, AlignmentI[] views)
398 switch (edit.command)
416 // TODO:add deleteNuc for UNDO
418 // insertNuc(edits[e]);
426 final public void doCommand(AlignmentI[] views)
428 performEdit(0, views);
432 * Undo the stored list of commands, in reverse order.
435 final public void undoCommand(AlignmentI[] views)
437 ListIterator<Edit> iterator = edits.listIterator(edits.size());
438 while (iterator.hasPrevious())
440 Edit e = iterator.previous();
468 * Insert gap(s) in sequences as specified by the command, and adjust
473 final private static void insertGap(Edit command)
476 for (int s = 0; s < command.seqs.length; s++)
478 command.seqs[s].insertCharAt(command.position, command.number,
480 // System.out.println("pos: "+command.position+" number:
481 // "+command.number);
484 adjustAnnotations(command, true, false, null);
488 // final void insertNuc(Edit command)
491 // for (int s = 0; s < command.seqs.length; s++)
493 // System.out.println("pos: "+command.position+" number: "+command.number);
494 // command.seqs[s].insertCharAt(command.position, command.number,'A');
497 // adjustAnnotations(command, true, false, null);
501 * Delete gap(s) in sequences as specified by the command, and adjust
506 final static private void deleteGap(Edit command)
508 for (int s = 0; s < command.seqs.length; s++)
510 command.seqs[s].deleteChars(command.position,
511 command.position + command.number);
514 adjustAnnotations(command, false, false, null);
518 * Carry out a Cut action. The cut characters are saved in case Undo is
524 static void cut(Edit command, AlignmentI[] views)
526 boolean seqDeleted = false;
527 command.string = new char[command.seqs.length][];
529 for (int i = 0; i < command.seqs.length; i++)
531 final SequenceI sequence = command.seqs[i];
532 if (sequence.getLength() > command.position)
534 command.string[i] = sequence.getSequence(command.position,
535 command.position + command.number);
536 SequenceI oldds = sequence.getDatasetSequence();
537 if (command.oldds != null && command.oldds[i] != null)
539 // we are redoing an undone cut.
540 sequence.setDatasetSequence(null);
542 sequence.deleteChars(command.position,
543 command.position + command.number);
544 if (command.oldds != null && command.oldds[i] != null)
546 // oldds entry contains the cut dataset sequence.
547 sequence.setDatasetSequence(command.oldds[i]);
548 command.oldds[i] = oldds;
552 // modify the oldds if necessary
553 if (oldds != sequence.getDatasetSequence()
554 || sequence.getFeatures().hasFeatures())
556 if (command.oldds == null)
558 command.oldds = new SequenceI[command.seqs.length];
560 command.oldds[i] = oldds;
561 // FIXME JAL-2541 JAL-2526 get correct positions if on a gap
565 sequence.findPosition(command.position),
566 sequence.findPosition(command.position + command.number),
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 (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
652 start = command.seqs[i].findPosition(command.position);
653 end = command.seqs[i]
654 .findPosition(command.position + command.number);
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 jalview.analysis.AlignSeq.extractGaps(
687 jalview.util.Comparison.GapChars,
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 = jalview.analysis.AlignSeq.extractGaps(
738 jalview.util.Comparison.GapChars,
739 new String(command.string[i]));
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 = jalview.analysis.AlignSeq.extractGaps(
746 jalview.util.Comparison.GapChars,
747 new String(command.string[i]));
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 command.seqs[s].sequenceChanged();
806 if (modifyVisibility)
808 // Rows are only removed or added to sequence object.
812 tmp = command.seqs[s].getAnnotation();
815 int alen = tmp.length;
816 for (int aa = 0; aa < tmp.length; aa++)
818 if (!command.al.deleteAnnotation(tmp[aa]))
820 // strip out annotation not in the current al (will be put
821 // back on insert in all views)
826 command.seqs[s].setAlignmentAnnotation(null);
827 if (alen != tmp.length)
829 // save the non-null annotation references only
830 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
831 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
835 saved[aapos++] = tmp[aa];
840 command.deletedAnnotationRows.put(command.seqs[s], saved);
841 // and then remove any annotation in the other views
842 for (int alview = 0; views != null
843 && alview < views.length; alview++)
845 if (views[alview] != command.al)
847 AlignmentAnnotation[] toremove = views[alview]
848 .getAlignmentAnnotation();
849 if (toremove == null || toremove.length == 0)
853 // remove any alignment annotation on this sequence that's
854 // on that alignment view.
855 for (int aa = 0; aa < toremove.length; aa++)
857 if (toremove[aa].sequenceRef == command.seqs[s])
859 views[alview].deleteAnnotation(toremove[aa]);
867 // save all the annotation
868 command.deletedAnnotationRows.put(command.seqs[s], tmp);
875 if (command.deletedAnnotationRows != null
876 && command.deletedAnnotationRows
877 .containsKey(command.seqs[s]))
879 AlignmentAnnotation[] revealed = command.deletedAnnotationRows
880 .get(command.seqs[s]);
881 command.seqs[s].setAlignmentAnnotation(revealed);
882 if (revealed != null)
884 for (int aa = 0; aa < revealed.length; aa++)
886 // iterate through al adding original annotation
887 command.al.addAnnotation(revealed[aa]);
889 for (int aa = 0; aa < revealed.length; aa++)
891 command.al.setAnnotationIndex(revealed[aa], aa);
893 // and then duplicate added annotation on every other alignment
895 for (int vnum = 0; views != null
896 && vnum < views.length; vnum++)
898 if (views[vnum] != command.al)
900 int avwidth = views[vnum].getWidth() + 1;
901 // duplicate in this view
902 for (int a = 0; a < revealed.length; a++)
904 AlignmentAnnotation newann = new AlignmentAnnotation(
906 command.seqs[s].addAlignmentAnnotation(newann);
907 newann.padAnnotation(avwidth);
908 views[vnum].addAnnotation(newann);
909 views[vnum].setAnnotationIndex(newann, a);
919 if (command.seqs[s].getAnnotation() == null)
926 annotations = command.seqs[s].getAnnotation();
930 tmp = new AlignmentAnnotation[aSize
931 + command.seqs[s].getAnnotation().length];
933 System.arraycopy(annotations, 0, tmp, 0, aSize);
935 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
936 command.seqs[s].getAnnotation().length);
940 aSize = annotations.length;
944 if (annotations == null)
951 command.deletedAnnotations = new Hashtable<String, Annotation[]>();
956 for (int a = 0; a < annotations.length; a++)
958 if (annotations[a].autoCalculated
959 || annotations[a].annotations == null)
966 aSize = annotations[a].annotations.length;
969 temp = new Annotation[aSize + command.number];
970 if (annotations[a].padGaps)
972 for (int aa = 0; aa < temp.length; aa++)
974 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
980 if (command.position < aSize)
982 if (command.position + command.number >= aSize)
988 tSize = aSize - command.number;
1000 temp = new Annotation[tSize];
1005 if (command.position < annotations[a].annotations.length)
1007 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1010 if (command.deletedAnnotations != null
1011 && command.deletedAnnotations
1012 .containsKey(annotations[a].annotationId))
1014 Annotation[] restore = command.deletedAnnotations
1015 .get(annotations[a].annotationId);
1017 System.arraycopy(restore, 0, temp, command.position,
1022 System.arraycopy(annotations[a].annotations, command.position,
1023 temp, command.position + command.number,
1024 aSize - command.position);
1028 if (command.deletedAnnotations != null
1029 && command.deletedAnnotations
1030 .containsKey(annotations[a].annotationId))
1032 Annotation[] restore = command.deletedAnnotations
1033 .get(annotations[a].annotationId);
1035 temp = new Annotation[annotations[a].annotations.length
1037 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1038 annotations[a].annotations.length);
1039 System.arraycopy(restore, 0, temp,
1040 annotations[a].annotations.length, restore.length);
1044 temp = annotations[a].annotations;
1050 if (tSize != aSize || command.position < 2)
1052 int copylen = Math.min(command.position,
1053 annotations[a].annotations.length);
1056 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1057 copylen); // command.position);
1060 Annotation[] deleted = new Annotation[command.number];
1061 if (copylen >= command.position)
1063 copylen = Math.min(command.number,
1064 annotations[a].annotations.length - command.position);
1067 System.arraycopy(annotations[a].annotations, command.position,
1068 deleted, 0, copylen); // command.number);
1072 command.deletedAnnotations.put(annotations[a].annotationId,
1074 if (annotations[a].annotations.length > command.position
1077 System.arraycopy(annotations[a].annotations,
1078 command.position + command.number, temp,
1079 command.position, annotations[a].annotations.length
1080 - command.position - command.number); // aSize
1085 int dSize = aSize - command.position;
1089 Annotation[] deleted = new Annotation[command.number];
1090 System.arraycopy(annotations[a].annotations, command.position,
1093 command.deletedAnnotations.put(annotations[a].annotationId,
1096 tSize = Math.min(annotations[a].annotations.length,
1098 temp = new Annotation[tSize];
1099 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
1103 temp = annotations[a].annotations;
1108 annotations[a].annotations = temp;
1112 final static void adjustFeatures(Edit command, int index, final int i,
1113 final int j, boolean insert)
1115 SequenceI seq = command.seqs[index];
1116 SequenceI sequence = seq.getDatasetSequence();
1117 if (sequence == null)
1124 if (command.editedFeatures != null
1125 && command.editedFeatures.containsKey(seq))
1127 sequence.setSequenceFeatures(command.editedFeatures.get(seq));
1133 List<SequenceFeature> sf = sequence.getFeatures()
1134 .getPositionalFeatures();
1141 List<SequenceFeature> oldsf = new ArrayList<SequenceFeature>();
1145 for (SequenceFeature feature : sf)
1147 SequenceFeature copy = new SequenceFeature(feature);
1151 if (feature.getEnd() < i)
1156 if (feature.getBegin() > j)
1158 int newBegin = copy.getBegin() - cSize;
1159 int newEnd = copy.getEnd() - cSize;
1160 SequenceFeature newSf = new SequenceFeature(feature, newBegin,
1161 newEnd, feature.getFeatureGroup(), feature.getScore());
1162 sequence.deleteFeature(feature);
1163 sequence.addSequenceFeature(newSf);
1164 // feature.setBegin(newBegin);
1165 // feature.setEnd(newEnd);
1169 int newBegin = feature.getBegin();
1170 int newEnd = feature.getEnd();
1174 // feature.setBegin(i);
1180 // feature.setEnd(j - 1);
1182 newEnd = newEnd - cSize;
1183 // feature.setEnd(feature.getEnd() - (cSize));
1185 sequence.deleteFeature(feature);
1186 if (newEnd >= newBegin)
1188 sequence.addSequenceFeature(new SequenceFeature(feature, newBegin,
1189 newEnd, feature.getFeatureGroup(), feature.getScore()));
1191 // if (feature.getBegin() > feature.getEnd())
1193 // sequence.deleteFeature(feature);
1197 if (command.editedFeatures == null)
1199 command.editedFeatures = new Hashtable<SequenceI, List<SequenceFeature>>();
1202 command.editedFeatures.put(seq, oldsf);
1207 * Returns the list of edit commands wrapped by this object.
1211 public List<Edit> getEdits()
1217 * Returns a map whose keys are the dataset sequences, and values their
1218 * aligned sequences before the command edit list was applied. The aligned
1219 * sequences are copies, which may be updated without affecting the originals.
1221 * The command holds references to the aligned sequences (after editing). If
1222 * the command is an 'undo',then the prior state is simply the aligned state.
1223 * Otherwise, we have to derive the prior state by working backwards through
1224 * the edit list to infer the aligned sequences before editing.
1226 * Note: an alternative solution would be to cache the 'before' state of each
1227 * edit, but this would be expensive in space in the common case that the
1228 * original is never needed (edits are not mirrored).
1231 * @throws IllegalStateException
1232 * on detecting an edit command of a type that can't be unwound
1234 public Map<SequenceI, SequenceI> priorState(boolean forUndo)
1236 Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
1237 if (getEdits() == null)
1243 for (Edit e : getEdits())
1245 for (SequenceI seq : e.getSequences())
1247 SequenceI ds = seq.getDatasetSequence();
1248 // SequenceI preEdit = result.get(ds);
1249 if (!result.containsKey(ds))
1252 * copy sequence including start/end (but don't use copy constructor
1253 * as we don't need annotations)
1255 SequenceI preEdit = new Sequence("", seq.getSequenceAsString(),
1256 seq.getStart(), seq.getEnd());
1257 preEdit.setDatasetSequence(ds);
1258 result.put(ds, preEdit);
1266 * Work backwards through the edit list, deriving the sequences before each
1267 * was applied. The final result is the sequence set before any edits.
1269 Iterator<Edit> editList = new ReverseListIterator<Edit>(getEdits());
1270 while (editList.hasNext())
1272 Edit oldEdit = editList.next();
1273 Action action = oldEdit.getAction();
1274 int position = oldEdit.getPosition();
1275 int number = oldEdit.getNumber();
1276 final char gap = oldEdit.getGapCharacter();
1277 for (SequenceI seq : oldEdit.getSequences())
1279 SequenceI ds = seq.getDatasetSequence();
1280 SequenceI preEdit = result.get(ds);
1281 if (preEdit == null)
1283 preEdit = new Sequence("", seq.getSequenceAsString(),
1284 seq.getStart(), seq.getEnd());
1285 preEdit.setDatasetSequence(ds);
1286 result.put(ds, preEdit);
1289 * 'Undo' this edit action on the sequence (updating the value in the
1294 if (action == Action.DELETE_GAP)
1296 preEdit.setSequence(new String(StringUtils.insertCharAt(
1297 preEdit.getSequence(), position, number, gap)));
1299 else if (action == Action.INSERT_GAP)
1301 preEdit.setSequence(new String(StringUtils.deleteChars(
1302 preEdit.getSequence(), position, position + number)));
1306 System.err.println("Can't undo edit action " + action);
1307 // throw new IllegalStateException("Can't undo edit action " +
1318 public SequenceI[] oldds;
1320 boolean fullAlignmentHeight = false;
1322 Hashtable<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
1324 Hashtable<String, Annotation[]> deletedAnnotations;
1326 Hashtable<SequenceI, List<SequenceFeature>> editedFeatures;
1338 int position, number;
1342 public Edit(Action cmd, SequenceI[] sqs, int pos, int count,
1347 this.position = pos;
1348 this.number = count;
1352 Edit(Action cmd, SequenceI[] sqs, int pos, int count,
1355 this.gapChar = align.getGapCharacter();
1358 this.position = pos;
1359 this.number = count;
1362 alIndex = new int[sqs.length];
1363 for (int i = 0; i < sqs.length; i++)
1365 alIndex[i] = align.findIndex(sqs[i]);
1368 fullAlignmentHeight = (align.getHeight() == sqs.length);
1371 Edit(Action cmd, SequenceI[] sqs, int pos, int count,
1372 AlignmentI align, String replace)
1376 this.position = pos;
1377 this.number = count;
1379 this.gapChar = align.getGapCharacter();
1380 string = new char[sqs.length][];
1381 for (int i = 0; i < sqs.length; i++)
1383 string[i] = replace.toCharArray();
1386 fullAlignmentHeight = (align.getHeight() == sqs.length);
1389 public SequenceI[] getSequences()
1394 public int getPosition()
1399 public Action getAction()
1404 public int getNumber()
1409 public char getGapCharacter()
1416 * Returns an iterator over the list of edit commands which traverses the list
1417 * either forwards or backwards.
1422 public Iterator<Edit> getEditIterator(boolean forwards)
1426 return getEdits().iterator();
1430 return new ReverseListIterator<Edit>(getEdits());