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>, 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.getFeatures().hasFeatures())
553 if (command.oldds == null)
555 command.oldds = new SequenceI[command.seqs.length];
557 command.oldds[i] = oldds;
558 // FIXME JAL-2541 JAL-2526 get correct positions if on a gap
562 sequence.findPosition(command.position),
563 sequence.findPosition(command.position + command.number),
569 if (sequence.getLength() < 1)
571 command.al.deleteSequence(sequence);
576 adjustAnnotations(command, false, seqDeleted, views);
580 * Perform the given Paste command. This may be to add cut or copied sequences
581 * to an alignment, or to undo a 'Cut' action on a region of the alignment.
586 static void paste(Edit command, AlignmentI[] views)
590 boolean newDSWasNeeded;
591 int newstart, newend;
592 boolean seqWasDeleted = false;
593 int start = 0, end = 0;
595 for (int i = 0; i < command.seqs.length; i++)
598 newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
599 if (command.seqs[i].getLength() < 1)
601 // ie this sequence was deleted, we need to
602 // readd it to the alignment
603 if (command.alIndex[i] < command.al.getHeight())
605 List<SequenceI> sequences;
606 synchronized (sequences = command.al.getSequences())
608 if (!(command.alIndex[i] < 0))
610 sequences.add(command.alIndex[i], command.seqs[i]);
616 command.al.addSequence(command.seqs[i]);
618 seqWasDeleted = true;
620 newstart = command.seqs[i].getStart();
621 newend = command.seqs[i].getEnd();
623 tmp = new StringBuffer();
624 tmp.append(command.seqs[i].getSequence());
625 // Undo of a delete does not replace original dataset sequence on to
626 // alignment sequence.
628 if (command.string != null && command.string[i] != null)
630 if (command.position >= tmp.length())
632 // This occurs if padding is on, and residues
633 // are removed from end of alignment
634 int length = command.position - tmp.length();
637 tmp.append(command.gapChar);
641 tmp.insert(command.position, command.string[i]);
642 for (int s = 0; s < command.string[i].length; s++)
644 if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
649 start = command.seqs[i].findPosition(command.position);
650 end = command.seqs[i].findPosition(command.position
653 if (command.seqs[i].getStart() == start)
663 command.string[i] = null;
666 command.seqs[i].setSequence(tmp.toString());
667 command.seqs[i].setStart(newstart);
668 command.seqs[i].setEnd(newend);
671 if (command.seqs[i].getDatasetSequence() != null)
676 ds = command.oldds[i];
680 // make a new DS sequence
681 // use new ds mechanism here
682 ds = new Sequence(command.seqs[i].getName(),
683 jalview.analysis.AlignSeq.extractGaps(
684 jalview.util.Comparison.GapChars,
685 command.seqs[i].getSequenceAsString()),
686 command.seqs[i].getStart(), command.seqs[i].getEnd());
687 ds.setDescription(command.seqs[i].getDescription());
689 if (command.oldds == null)
691 command.oldds = new SequenceI[command.seqs.length];
693 command.oldds[i] = command.seqs[i].getDatasetSequence();
694 command.seqs[i].setDatasetSequence(ds);
696 adjustFeatures(command, i, start, end, true);
699 adjustAnnotations(command, true, seqWasDeleted, views);
701 command.string = null;
704 static void replace(Edit command)
708 int start = command.position;
709 int end = command.number;
710 // TODO TUTORIAL - Fix for replacement with different length of sequence (or
712 // TODO Jalview 2.4 bugfix change to an aggregate command - original
713 // sequence string is cut, new string is pasted in.
714 command.number = start + command.string[0].length;
715 for (int i = 0; i < command.seqs.length; i++)
717 boolean newDSWasNeeded = command.oldds != null
718 && command.oldds[i] != null;
721 * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
722 * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
723 * viewport.alignment));
727 * then addHistoryItem(new EditCommand( "Add sequences",
728 * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
731 oldstring = command.seqs[i].getSequenceAsString();
732 tmp = new StringBuffer(oldstring.substring(0, start));
733 tmp.append(command.string[i]);
734 String nogaprep = jalview.analysis.AlignSeq.extractGaps(
735 jalview.util.Comparison.GapChars, new String(
737 int ipos = command.seqs[i].findPosition(start)
738 - command.seqs[i].getStart();
739 tmp.append(oldstring.substring(end));
740 command.seqs[i].setSequence(tmp.toString());
741 command.string[i] = oldstring.substring(start, end).toCharArray();
742 String nogapold = jalview.analysis.AlignSeq.extractGaps(
743 jalview.util.Comparison.GapChars, new String(
745 if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
749 SequenceI oldds = command.seqs[i].getDatasetSequence();
750 command.seqs[i].setDatasetSequence(command.oldds[i]);
751 command.oldds[i] = oldds;
755 if (command.oldds == null)
757 command.oldds = new SequenceI[command.seqs.length];
759 command.oldds[i] = command.seqs[i].getDatasetSequence();
760 SequenceI newds = new Sequence(
761 command.seqs[i].getDatasetSequence());
762 String fullseq, osp = newds.getSequenceAsString();
763 fullseq = osp.substring(0, ipos) + nogaprep
764 + osp.substring(ipos + nogaprep.length());
765 newds.setSequence(fullseq.toUpperCase());
766 // TODO: JAL-1131 ensure newly created dataset sequence is added to
768 // dataset sequences associated with the alignment.
769 // TODO: JAL-1131 fix up any annotation associated with new dataset
770 // sequence to ensure that original sequence/annotation relationships
772 command.seqs[i].setDatasetSequence(newds);
781 final static void adjustAnnotations(Edit command, boolean insert,
782 boolean modifyVisibility, AlignmentI[] views)
784 AlignmentAnnotation[] annotations = null;
786 if (modifyVisibility && !insert)
788 // only occurs if a sequence was added or deleted.
789 command.deletedAnnotationRows = new Hashtable<SequenceI, AlignmentAnnotation[]>();
791 if (command.fullAlignmentHeight)
793 annotations = command.al.getAlignmentAnnotation();
798 AlignmentAnnotation[] tmp;
799 for (int s = 0; s < command.seqs.length; s++)
801 command.seqs[s].sequenceChanged();
803 if (modifyVisibility)
805 // Rows are only removed or added to sequence object.
809 tmp = command.seqs[s].getAnnotation();
812 int alen = tmp.length;
813 for (int aa = 0; aa < tmp.length; aa++)
815 if (!command.al.deleteAnnotation(tmp[aa]))
817 // strip out annotation not in the current al (will be put
818 // back on insert in all views)
823 command.seqs[s].setAlignmentAnnotation(null);
824 if (alen != tmp.length)
826 // save the non-null annotation references only
827 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
828 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
832 saved[aapos++] = tmp[aa];
837 command.deletedAnnotationRows.put(command.seqs[s], saved);
838 // and then remove any annotation in the other views
839 for (int alview = 0; views != null && alview < views.length; alview++)
841 if (views[alview] != command.al)
843 AlignmentAnnotation[] toremove = views[alview]
844 .getAlignmentAnnotation();
845 if (toremove == null || toremove.length == 0)
849 // remove any alignment annotation on this sequence that's
850 // on that alignment view.
851 for (int aa = 0; aa < toremove.length; aa++)
853 if (toremove[aa].sequenceRef == command.seqs[s])
855 views[alview].deleteAnnotation(toremove[aa]);
863 // save all the annotation
864 command.deletedAnnotationRows.put(command.seqs[s], tmp);
871 if (command.deletedAnnotationRows != null
872 && command.deletedAnnotationRows
873 .containsKey(command.seqs[s]))
875 AlignmentAnnotation[] revealed = command.deletedAnnotationRows
876 .get(command.seqs[s]);
877 command.seqs[s].setAlignmentAnnotation(revealed);
878 if (revealed != null)
880 for (int aa = 0; aa < revealed.length; aa++)
882 // iterate through al adding original annotation
883 command.al.addAnnotation(revealed[aa]);
885 for (int aa = 0; aa < revealed.length; aa++)
887 command.al.setAnnotationIndex(revealed[aa], aa);
889 // and then duplicate added annotation on every other alignment
891 for (int vnum = 0; views != null && vnum < views.length; vnum++)
893 if (views[vnum] != command.al)
895 int avwidth = views[vnum].getWidth() + 1;
896 // duplicate in this view
897 for (int a = 0; a < revealed.length; a++)
899 AlignmentAnnotation newann = new AlignmentAnnotation(
901 command.seqs[s].addAlignmentAnnotation(newann);
902 newann.padAnnotation(avwidth);
903 views[vnum].addAnnotation(newann);
904 views[vnum].setAnnotationIndex(newann, a);
914 if (command.seqs[s].getAnnotation() == null)
921 annotations = command.seqs[s].getAnnotation();
925 tmp = new AlignmentAnnotation[aSize
926 + command.seqs[s].getAnnotation().length];
928 System.arraycopy(annotations, 0, tmp, 0, aSize);
930 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
931 command.seqs[s].getAnnotation().length);
935 aSize = annotations.length;
939 if (annotations == null)
946 command.deletedAnnotations = new Hashtable<String, Annotation[]>();
951 for (int a = 0; a < annotations.length; a++)
953 if (annotations[a].autoCalculated
954 || annotations[a].annotations == null)
961 aSize = annotations[a].annotations.length;
964 temp = new Annotation[aSize + command.number];
965 if (annotations[a].padGaps)
967 for (int aa = 0; aa < temp.length; aa++)
969 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
975 if (command.position < aSize)
977 if (command.position + command.number >= aSize)
983 tSize = aSize - command.number;
995 temp = new Annotation[tSize];
1000 if (command.position < annotations[a].annotations.length)
1002 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1005 if (command.deletedAnnotations != null
1006 && command.deletedAnnotations
1007 .containsKey(annotations[a].annotationId))
1009 Annotation[] restore = command.deletedAnnotations
1010 .get(annotations[a].annotationId);
1012 System.arraycopy(restore, 0, temp, command.position,
1017 System.arraycopy(annotations[a].annotations, command.position,
1018 temp, command.position + command.number, aSize
1019 - command.position);
1023 if (command.deletedAnnotations != null
1024 && command.deletedAnnotations
1025 .containsKey(annotations[a].annotationId))
1027 Annotation[] restore = command.deletedAnnotations
1028 .get(annotations[a].annotationId);
1030 temp = new Annotation[annotations[a].annotations.length
1032 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1033 annotations[a].annotations.length);
1034 System.arraycopy(restore, 0, temp,
1035 annotations[a].annotations.length, restore.length);
1039 temp = annotations[a].annotations;
1045 if (tSize != aSize || command.position < 2)
1047 int copylen = Math.min(command.position,
1048 annotations[a].annotations.length);
1051 System.arraycopy(annotations[a].annotations, 0, temp, 0,
1052 copylen); // command.position);
1055 Annotation[] deleted = new Annotation[command.number];
1056 if (copylen >= command.position)
1058 copylen = Math.min(command.number,
1059 annotations[a].annotations.length - command.position);
1062 System.arraycopy(annotations[a].annotations,
1063 command.position, deleted, 0, copylen); // command.number);
1067 command.deletedAnnotations.put(annotations[a].annotationId,
1069 if (annotations[a].annotations.length > command.position
1072 System.arraycopy(annotations[a].annotations, command.position
1073 + command.number, temp, command.position,
1074 annotations[a].annotations.length - command.position
1075 - command.number); // aSize
1080 int dSize = aSize - command.position;
1084 Annotation[] deleted = new Annotation[command.number];
1085 System.arraycopy(annotations[a].annotations, command.position,
1088 command.deletedAnnotations.put(annotations[a].annotationId,
1091 tSize = Math.min(annotations[a].annotations.length,
1093 temp = new Annotation[tSize];
1094 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
1098 temp = annotations[a].annotations;
1103 annotations[a].annotations = temp;
1107 final static void adjustFeatures(Edit command, int index, final int i,
1108 final int j, boolean insert)
1110 SequenceI seq = command.seqs[index];
1111 SequenceI sequence = seq.getDatasetSequence();
1112 if (sequence == null)
1119 if (command.editedFeatures != null
1120 && command.editedFeatures.containsKey(seq))
1122 sequence.setSequenceFeatures(command.editedFeatures.get(seq));
1128 List<SequenceFeature> sf = sequence.getFeatures()
1129 .getPositionalFeatures();
1136 List<SequenceFeature> oldsf = new ArrayList<SequenceFeature>();
1140 for (SequenceFeature feature : sf)
1142 SequenceFeature copy = new SequenceFeature(feature);
1146 if (feature.getEnd() < i)
1151 if (feature.getBegin() > j)
1153 int newBegin = copy.getBegin() - cSize;
1154 int newEnd = copy.getEnd() - cSize;
1155 SequenceFeature newSf = new SequenceFeature(feature, newBegin,
1156 newEnd, feature.getFeatureGroup(), feature.getScore());
1157 sequence.deleteFeature(feature);
1158 sequence.addSequenceFeature(newSf);
1159 // feature.setBegin(newBegin);
1160 // feature.setEnd(newEnd);
1164 int newBegin = feature.getBegin();
1165 int newEnd = feature.getEnd();
1169 // feature.setBegin(i);
1175 // feature.setEnd(j - 1);
1177 newEnd = newEnd - cSize;
1178 // feature.setEnd(feature.getEnd() - (cSize));
1180 sequence.deleteFeature(feature);
1181 if (newEnd >= newBegin)
1183 sequence.addSequenceFeature(new SequenceFeature(feature, newBegin,
1184 newEnd, feature.getFeatureGroup(), feature.getScore()));
1186 // if (feature.getBegin() > feature.getEnd())
1188 // sequence.deleteFeature(feature);
1192 if (command.editedFeatures == null)
1194 command.editedFeatures = new Hashtable<SequenceI, List<SequenceFeature>>();
1197 command.editedFeatures.put(seq, oldsf);
1202 * Returns the list of edit commands wrapped by this object.
1206 public List<Edit> getEdits()
1212 * Returns a map whose keys are the dataset sequences, and values their
1213 * aligned sequences before the command edit list was applied. The aligned
1214 * sequences are copies, which may be updated without affecting the originals.
1216 * The command holds references to the aligned sequences (after editing). If
1217 * the command is an 'undo',then the prior state is simply the aligned state.
1218 * Otherwise, we have to derive the prior state by working backwards through
1219 * the edit list to infer the aligned sequences before editing.
1221 * Note: an alternative solution would be to cache the 'before' state of each
1222 * edit, but this would be expensive in space in the common case that the
1223 * original is never needed (edits are not mirrored).
1226 * @throws IllegalStateException
1227 * on detecting an edit command of a type that can't be unwound
1229 public Map<SequenceI, SequenceI> priorState(boolean forUndo)
1231 Map<SequenceI, SequenceI> result = new HashMap<SequenceI, SequenceI>();
1232 if (getEdits() == null)
1238 for (Edit e : getEdits())
1240 for (SequenceI seq : e.getSequences())
1242 SequenceI ds = seq.getDatasetSequence();
1243 // SequenceI preEdit = result.get(ds);
1244 if (!result.containsKey(ds))
1247 * copy sequence including start/end (but don't use copy constructor
1248 * as we don't need annotations)
1250 SequenceI preEdit = new Sequence("", seq.getSequenceAsString(),
1251 seq.getStart(), seq.getEnd());
1252 preEdit.setDatasetSequence(ds);
1253 result.put(ds, preEdit);
1261 * Work backwards through the edit list, deriving the sequences before each
1262 * was applied. The final result is the sequence set before any edits.
1264 Iterator<Edit> editList = new ReverseListIterator<Edit>(getEdits());
1265 while (editList.hasNext())
1267 Edit oldEdit = editList.next();
1268 Action action = oldEdit.getAction();
1269 int position = oldEdit.getPosition();
1270 int number = oldEdit.getNumber();
1271 final char gap = oldEdit.getGapCharacter();
1272 for (SequenceI seq : oldEdit.getSequences())
1274 SequenceI ds = seq.getDatasetSequence();
1275 SequenceI preEdit = result.get(ds);
1276 if (preEdit == null)
1278 preEdit = new Sequence("", seq.getSequenceAsString(),
1279 seq.getStart(), seq.getEnd());
1280 preEdit.setDatasetSequence(ds);
1281 result.put(ds, preEdit);
1284 * 'Undo' this edit action on the sequence (updating the value in the
1289 if (action == Action.DELETE_GAP)
1291 preEdit.setSequence(new String(StringUtils.insertCharAt(
1292 preEdit.getSequence(), position, number, gap)));
1294 else if (action == Action.INSERT_GAP)
1296 preEdit.setSequence(new String(StringUtils.deleteChars(
1297 preEdit.getSequence(), position, position + number)));
1301 System.err.println("Can't undo edit action " + action);
1302 // throw new IllegalStateException("Can't undo edit action " +
1313 public SequenceI[] oldds;
1315 boolean fullAlignmentHeight = false;
1317 Hashtable<SequenceI, AlignmentAnnotation[]> deletedAnnotationRows;
1319 Hashtable<String, Annotation[]> deletedAnnotations;
1321 Hashtable<SequenceI, List<SequenceFeature>> editedFeatures;
1333 int position, number;
1337 public Edit(Action cmd, SequenceI[] sqs, int pos, int count,
1342 this.position = pos;
1343 this.number = count;
1347 Edit(Action cmd, SequenceI[] sqs, int pos, int count,
1350 this.gapChar = align.getGapCharacter();
1353 this.position = pos;
1354 this.number = count;
1357 alIndex = new int[sqs.length];
1358 for (int i = 0; i < sqs.length; i++)
1360 alIndex[i] = align.findIndex(sqs[i]);
1363 fullAlignmentHeight = (align.getHeight() == sqs.length);
1366 Edit(Action cmd, SequenceI[] sqs, int pos, int count,
1367 AlignmentI align, String replace)
1371 this.position = pos;
1372 this.number = count;
1374 this.gapChar = align.getGapCharacter();
1375 string = new char[sqs.length][];
1376 for (int i = 0; i < sqs.length; i++)
1378 string[i] = replace.toCharArray();
1381 fullAlignmentHeight = (align.getHeight() == sqs.length);
1384 public SequenceI[] getSequences()
1389 public int getPosition()
1394 public Action getAction()
1399 public int getNumber()
1404 public char getGapCharacter()
1411 * Returns an iterator over the list of edit commands which traverses the list
1412 * either forwards or backwards.
1417 public Iterator<Edit> getEditIterator(boolean forwards)
1421 return getEdits().iterator();
1425 return new ReverseListIterator<Edit>(getEdits());