2 * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3 * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 package jalview.commands;
23 import jalview.datamodel.*;
32 * Description: Essential information for performing undo and redo for cut/paste
33 * insert/delete gap which can be stored in the HistoryList
37 * Copyright: Copyright (c) 2006
41 * Company: Dundee University
44 * @author not attributable
47 public class EditCommand implements CommandI
49 public static final int INSERT_GAP = 0;
51 public static final int DELETE_GAP = 1;
53 public static final int CUT = 2;
55 public static final int PASTE = 3;
57 public static final int REPLACE = 4;
67 public EditCommand(String description)
69 this.description = description;
72 public EditCommand(String description, int command, SequenceI[] seqs,
73 int position, int number, AlignmentI al)
75 this.description = description;
76 if (command == CUT || command == PASTE)
79 { new Edit(command, seqs, position, number, al) };
85 public EditCommand(String description, int command, String replace,
86 SequenceI[] seqs, int position, int number, AlignmentI al)
88 this.description = description;
89 if (command == REPLACE)
92 { new Edit(command, seqs, position, number, al, replace) };
98 final public String getDescription()
105 return edits == null ? 0 : edits.length;
108 final public AlignmentI getAlignment()
114 * append a new editCommand Note. this shouldn't be called if the edit is an
115 * operation affects more alignment objects than the one referenced in al (for
116 * example, cut or pasting whole sequences). Use the form with an additional
117 * AlignmentI[] views parameter.
126 final public void appendEdit(int command, SequenceI[] seqs, int position,
127 int number, AlignmentI al, boolean performEdit)
129 appendEdit(command, seqs, position, number, al, performEdit, null);
133 * append a new edit command with a set of alignment views that may be
144 final public void appendEdit(int command, SequenceI[] seqs, int position,
145 int number, AlignmentI al, boolean performEdit, AlignmentI[] views)
147 Edit edit = new Edit(command, seqs, position, number, al
149 if (al.getHeight() == seqs.length)
152 edit.fullAlignmentHeight = true;
157 Edit[] temp = new Edit[edits.length + 1];
158 System.arraycopy(edits, 0, temp, 0, edits.length);
160 edits[edits.length - 1] = edit;
170 performEdit(edits.length - 1, views);
174 final void performEdit(int commandIndex, AlignmentI[] views)
176 int eSize = edits.length;
177 for (int e = commandIndex; e < eSize; e++)
179 switch (edits[e].command)
188 cut(edits[e], views);
191 paste(edits[e], views);
200 final public void doCommand(AlignmentI[] views)
202 performEdit(0, views);
205 final public void undoCommand(AlignmentI[] views)
207 int e = 0, eSize = edits.length;
208 for (e = eSize - 1; e > -1; e--)
210 switch (edits[e].command)
219 paste(edits[e], views);
222 cut(edits[e], views);
231 final void insertGap(Edit command)
234 for (int s = 0; s < command.seqs.length; s++)
236 command.seqs[s].insertCharAt(command.position, command.number,
240 adjustAnnotations(command, true, false, null);
243 final void deleteGap(Edit command)
245 for (int s = 0; s < command.seqs.length; s++)
247 command.seqs[s].deleteChars(command.position, command.position
251 adjustAnnotations(command, false, false, null);
254 void cut(Edit command, AlignmentI[] views)
256 boolean seqDeleted = false;
257 command.string = new char[command.seqs.length][];
259 for (int i = 0; i < command.seqs.length; i++)
261 if (command.seqs[i].getLength() > command.position)
263 command.string[i] = command.seqs[i].getSequence(command.position,
264 command.position + command.number);
265 SequenceI oldds = command.seqs[i].getDatasetSequence();
266 if (command.oldds != null && command.oldds[i] != null)
268 // we are redoing an undone cut.
269 command.seqs[i].setDatasetSequence(null);
271 command.seqs[i].deleteChars(command.position, command.position
273 if (command.oldds != null && command.oldds[i] != null)
275 // oldds entry contains the cut dataset sequence.
276 command.seqs[i].setDatasetSequence(command.oldds[i]);
277 command.oldds[i] = oldds;
281 // modify the oldds if necessary
282 if (oldds != command.seqs[i].getDatasetSequence()
283 || command.seqs[i].getSequenceFeatures() != null)
285 if (command.oldds == null)
287 command.oldds = new SequenceI[command.seqs.length];
289 command.oldds[i] = oldds;
290 adjustFeatures(command, i, command.seqs[i]
291 .findPosition(command.position), command.seqs[i]
292 .findPosition(command.position + command.number), false);
297 if (command.seqs[i].getLength() < 1)
299 command.al.deleteSequence(command.seqs[i]);
304 adjustAnnotations(command, false, seqDeleted, views);
307 void paste(Edit command, AlignmentI[] views)
311 boolean newDSWasNeeded;
312 int newstart, newend;
313 boolean seqWasDeleted = false;
314 int start = 0, end = 0;
316 for (int i = 0; i < command.seqs.length; i++)
319 newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
320 if (command.seqs[i].getLength() < 1)
322 // ie this sequence was deleted, we need to
323 // read it to the alignment
324 if (command.alIndex[i] < command.al.getHeight())
326 command.al.getSequences().insertElementAt(command.seqs[i],
331 command.al.addSequence(command.seqs[i]);
333 seqWasDeleted = true;
335 newstart = command.seqs[i].getStart();
336 newend = command.seqs[i].getEnd();
338 tmp = new StringBuffer();
339 tmp.append(command.seqs[i].getSequence());
340 // Undo of a delete does not replace original dataset sequence on to
341 // alignment sequence.
343 if (command.string != null && command.string[i] != null)
345 if (command.position >= tmp.length())
347 // This occurs if padding is on, and residues
348 // are removed from end of alignment
349 int length = command.position - tmp.length();
352 tmp.append(command.gapChar);
356 tmp.insert(command.position, command.string[i]);
357 for (int s = 0; s < command.string[i].length; s++)
359 if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
364 start = command.seqs[i].findPosition(command.position);
365 end = command.seqs[i].findPosition(command.position
368 if (command.seqs[i].getStart() == start)
374 command.string[i] = null;
377 command.seqs[i].setSequence(tmp.toString());
378 command.seqs[i].setStart(newstart);
379 command.seqs[i].setEnd(newend);
382 if (command.seqs[i].getDatasetSequence() != null)
387 ds = command.oldds[i];
391 // make a new DS sequence
392 // use new ds mechanism here
393 ds = new Sequence(command.seqs[i].getName(),
394 jalview.analysis.AlignSeq.extractGaps(
395 jalview.util.Comparison.GapChars,
396 command.seqs[i].getSequenceAsString()),
397 command.seqs[i].getStart(), command.seqs[i].getEnd());
398 ds.setDescription(command.seqs[i].getDescription());
400 if (command.oldds == null)
402 command.oldds = new SequenceI[command.seqs.length];
404 command.oldds[i] = command.seqs[i].getDatasetSequence();
405 command.seqs[i].setDatasetSequence(ds);
407 adjustFeatures(command, i, start, end, true);
410 adjustAnnotations(command, true, seqWasDeleted, views);
412 command.string = null;
415 void replace(Edit command)
419 int start = command.position;
420 int end = command.number;
421 // TODO TUTORIAL - Fix for replacement with different length of sequence (or
423 // TODO Jalview 2.4 bugfix change to an aggregate command - original
424 // sequence string is cut, new string is pasted in.
425 command.number = start + command.string[0].length;
426 for (int i = 0; i < command.seqs.length; i++)
429 * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
430 * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
431 * viewport.alignment));
435 * then addHistoryItem(new EditCommand( "Add sequences",
436 * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
439 oldstring = command.seqs[i].getSequenceAsString();
440 tmp = new StringBuffer(oldstring.substring(0, start));
441 tmp.append(command.string[i]);
442 tmp.append(oldstring.substring(end));
443 command.seqs[i].setSequence(tmp.toString());
444 command.string[i] = oldstring.substring(start, end).toCharArray();
450 final void adjustAnnotations(Edit command, boolean insert,
451 boolean modifyVisibility, AlignmentI[] views)
453 AlignmentAnnotation[] annotations = null;
455 if (modifyVisibility && !insert)
457 // only occurs if a sequence was added or deleted.
458 command.deletedAnnotationRows = new Hashtable();
460 if (command.fullAlignmentHeight)
462 annotations = command.al.getAlignmentAnnotation();
467 AlignmentAnnotation[] tmp;
468 for (int s = 0; s < command.seqs.length; s++)
470 if (modifyVisibility)
472 // Rows are only removed or added to sequence object.
476 tmp = command.seqs[s].getAnnotation();
479 int alen = tmp.length;
480 for (int aa = 0; aa < tmp.length; aa++)
482 if (!command.al.deleteAnnotation(tmp[aa]))
484 // strip out annotation not in the current al (will be put
485 // back on insert in all views)
490 command.seqs[s].setAlignmentAnnotation(null);
491 if (alen != tmp.length)
493 // save the non-null annotation references only
494 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
495 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
499 saved[aapos++] = tmp[aa];
504 command.deletedAnnotationRows.put(command.seqs[s], saved);
505 // and then remove any annotation in the other views
506 for (int alview = 0; views != null && alview < views.length; alview++)
508 if (views[alview] != command.al)
510 AlignmentAnnotation[] toremove = views[alview]
511 .getAlignmentAnnotation();
512 if (toremove == null || toremove.length == 0)
516 // remove any alignment annotation on this sequence that's
517 // on that alignment view.
518 for (int aa = 0; aa < toremove.length; aa++)
520 if (toremove[aa].sequenceRef == command.seqs[s])
522 views[alview].deleteAnnotation(toremove[aa]);
530 // save all the annotation
531 command.deletedAnnotationRows.put(command.seqs[s], tmp);
538 if (command.deletedAnnotationRows != null
539 && command.deletedAnnotationRows
540 .containsKey(command.seqs[s]))
542 AlignmentAnnotation[] revealed = (AlignmentAnnotation[]) command.deletedAnnotationRows
543 .get(command.seqs[s]);
544 command.seqs[s].setAlignmentAnnotation(revealed);
545 if (revealed != null)
547 for (int aa = 0; aa < revealed.length; aa++)
549 // iterate through al adding original annotation
550 command.al.addAnnotation(revealed[aa]);
552 for (int aa = 0; aa < revealed.length; aa++)
554 command.al.setAnnotationIndex(revealed[aa], aa);
556 // and then duplicate added annotation on every other alignment
558 for (int vnum = 0; views != null && vnum < views.length; vnum++)
560 if (views[vnum] != command.al)
562 int avwidth = views[vnum].getWidth() + 1;
563 // duplicate in this view
564 for (int a = 0; a < revealed.length; a++)
566 AlignmentAnnotation newann = new AlignmentAnnotation(
568 command.seqs[s].addAlignmentAnnotation(newann);
569 newann.padAnnotation(avwidth);
570 views[vnum].addAnnotation(newann);
571 views[vnum].setAnnotationIndex(newann, a);
581 if (command.seqs[s].getAnnotation() == null)
588 annotations = command.seqs[s].getAnnotation();
592 tmp = new AlignmentAnnotation[aSize
593 + command.seqs[s].getAnnotation().length];
595 System.arraycopy(annotations, 0, tmp, 0, aSize);
597 System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
598 command.seqs[s].getAnnotation().length);
602 aSize = annotations.length;
606 if (annotations == null)
613 command.deletedAnnotations = new Hashtable();
618 for (int a = 0; a < annotations.length; a++)
620 if (annotations[a].autoCalculated
621 || annotations[a].annotations == null)
628 aSize = annotations[a].annotations.length;
631 temp = new Annotation[aSize + command.number];
632 if (annotations[a].padGaps)
633 for (int aa = 0; aa < temp.length; aa++)
635 temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
640 if (command.position < aSize)
642 if (command.position + command.number >= aSize)
648 tSize = aSize - command.number;
660 temp = new Annotation[tSize];
665 if (command.position < annotations[a].annotations.length)
667 System.arraycopy(annotations[a].annotations, 0, temp, 0,
670 if (command.deletedAnnotations != null
671 && command.deletedAnnotations
672 .containsKey(annotations[a].annotationId))
674 Annotation[] restore = (Annotation[]) command.deletedAnnotations
675 .get(annotations[a].annotationId);
677 System.arraycopy(restore, 0, temp, command.position,
682 System.arraycopy(annotations[a].annotations, command.position,
683 temp, command.position + command.number, aSize
688 if (command.deletedAnnotations != null
689 && command.deletedAnnotations
690 .containsKey(annotations[a].annotationId))
692 Annotation[] restore = (Annotation[]) command.deletedAnnotations
693 .get(annotations[a].annotationId);
695 temp = new Annotation[annotations[a].annotations.length
697 System.arraycopy(annotations[a].annotations, 0, temp, 0,
698 annotations[a].annotations.length);
699 System.arraycopy(restore, 0, temp,
700 annotations[a].annotations.length, restore.length);
704 temp = annotations[a].annotations;
710 if (tSize != aSize || command.position < 2)
712 int copylen = Math.min(command.position,
713 annotations[a].annotations.length);
715 System.arraycopy(annotations[a].annotations, 0, temp, 0,
716 copylen); // command.position);
718 Annotation[] deleted = new Annotation[command.number];
719 if (copylen >= command.position)
721 copylen = Math.min(command.number,
722 annotations[a].annotations.length - command.position);
725 System.arraycopy(annotations[a].annotations,
726 command.position, deleted, 0, copylen); // command.number);
730 command.deletedAnnotations.put(annotations[a].annotationId,
732 if (annotations[a].annotations.length > command.position
735 System.arraycopy(annotations[a].annotations, command.position
736 + command.number, temp, command.position,
737 annotations[a].annotations.length - command.position
738 - command.number); // aSize
743 int dSize = aSize - command.position;
747 Annotation[] deleted = new Annotation[command.number];
748 System.arraycopy(annotations[a].annotations, command.position,
751 command.deletedAnnotations.put(annotations[a].annotationId,
754 tSize = Math.min(annotations[a].annotations.length,
756 temp = new Annotation[tSize];
757 System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
761 temp = annotations[a].annotations;
766 annotations[a].annotations = temp;
770 final void adjustFeatures(Edit command, int index, int i, int j,
773 SequenceI seq = command.seqs[index];
774 SequenceI sequence = seq.getDatasetSequence();
775 if (sequence == null)
782 if (command.editedFeatures != null
783 && command.editedFeatures.containsKey(seq))
786 .setSequenceFeatures((SequenceFeature[]) command.editedFeatures
793 SequenceFeature[] sf = sequence.getSequenceFeatures();
800 SequenceFeature[] oldsf = new SequenceFeature[sf.length];
804 for (int s = 0; s < sf.length; s++)
806 SequenceFeature copy = new SequenceFeature(sf[s]);
810 if (sf[s].getEnd() < i)
815 if (sf[s].getBegin() > j)
817 sf[s].setBegin(copy.getBegin() - cSize);
818 sf[s].setEnd(copy.getEnd() - cSize);
822 if (sf[s].getBegin() >= i)
827 if (sf[s].getEnd() < j)
832 sf[s].setEnd(sf[s].getEnd() - (cSize));
834 if (sf[s].getBegin() > sf[s].getEnd())
836 sequence.deleteFeature(sf[s]);
840 if (command.editedFeatures == null)
842 command.editedFeatures = new Hashtable();
845 command.editedFeatures.put(seq, oldsf);
851 public SequenceI[] oldds;
853 boolean fullAlignmentHeight = false;
855 Hashtable deletedAnnotationRows;
857 Hashtable deletedAnnotations;
859 Hashtable editedFeatures;
871 int position, number;
875 Edit(int command, SequenceI[] seqs, int position, int number,
878 this.command = command;
880 this.position = position;
881 this.number = number;
882 this.gapChar = gapChar;
885 Edit(int command, SequenceI[] seqs, int position, int number,
888 this.gapChar = al.getGapCharacter();
889 this.command = command;
891 this.position = position;
892 this.number = number;
895 alIndex = new int[seqs.length];
896 for (int i = 0; i < seqs.length; i++)
898 alIndex[i] = al.findIndex(seqs[i]);
901 fullAlignmentHeight = (al.getHeight() == seqs.length);
904 Edit(int command, SequenceI[] seqs, int position, int number,
905 AlignmentI al, String replace)
907 this.command = command;
909 this.position = position;
910 this.number = number;
912 this.gapChar = al.getGapCharacter();
913 string = new char[seqs.length][];
914 for (int i = 0; i < seqs.length; i++)
916 string[i] = replace.toCharArray();
919 fullAlignmentHeight = (al.getHeight() == seqs.length);