From 5f090b7d1cb8836903474f8f4d5e475be142d30c Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 25 Nov 2014 16:55:09 +0000 Subject: [PATCH] JFAL-1594 fix Undo bug; enum EditCommand.Action; basic JUnit --- src/jalview/appletgui/APopupMenu.java | 54 +++- src/jalview/appletgui/AlignFrame.java | 9 +- src/jalview/appletgui/RedundancyPanel.java | 26 +- src/jalview/appletgui/SeqPanel.java | 58 +++-- src/jalview/commands/EditCommand.java | 303 +++++++++++++++-------- src/jalview/commands/RemoveGapColCommand.java | 11 +- src/jalview/commands/RemoveGapsCommand.java | 9 +- src/jalview/commands/SlideSequencesCommand.java | 31 ++- src/jalview/commands/TrimRegionCommand.java | 23 +- src/jalview/datamodel/ColumnSelection.java | 19 +- src/jalview/gui/AlignFrame.java | 18 +- src/jalview/gui/AlignViewport.java | 5 +- src/jalview/gui/PopupMenu.java | 3 +- src/jalview/gui/RedundancyPanel.java | 36 ++- src/jalview/gui/SeqPanel.java | 19 +- test/jalview/commands/EditCommandTest.java | 232 +++++++++++++++++ 16 files changed, 639 insertions(+), 217 deletions(-) create mode 100644 test/jalview/commands/EditCommandTest.java diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index eba1200..98a4a1c 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -20,19 +20,43 @@ */ package jalview.appletgui; -import java.util.*; - -import java.awt.*; -import java.awt.event.*; - -import jalview.analysis.*; -import jalview.commands.*; -import jalview.datamodel.*; -import jalview.schemes.*; -import jalview.util.MessageManager; -import jalview.util.UrlLink; +import jalview.analysis.AAFrequency; +import jalview.analysis.Conservation; +import jalview.commands.ChangeCaseCommand; +import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; import jalview.io.AppletFormatAdapter; import jalview.io.SequenceAnnotationReport; +import jalview.schemes.Blosum62ColourScheme; +import jalview.schemes.BuriedColourScheme; +import jalview.schemes.ClustalxColourScheme; +import jalview.schemes.HelixColourScheme; +import jalview.schemes.HydrophobicColourScheme; +import jalview.schemes.NucleotideColourScheme; +import jalview.schemes.PIDColourScheme; +import jalview.schemes.ResidueProperties; +import jalview.schemes.StrandColourScheme; +import jalview.schemes.TaylorColourScheme; +import jalview.schemes.TurnColourScheme; +import jalview.schemes.ZappoColourScheme; +import jalview.util.MessageManager; +import jalview.util.UrlLink; + +import java.awt.CheckboxMenuItem; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.Vector; public class APopupMenu extends java.awt.PopupMenu implements ActionListener, ItemListener @@ -549,7 +573,9 @@ public class APopupMenu extends java.awt.PopupMenu implements if (sg != null) { if (seq == null) + { seq = (Sequence) sg.getSequenceAt(0); + } EditNameDialog dialog = new EditNameDialog(seq.getSequenceAsString( sg.getStartRes(), sg.getEndRes() + 1), null, @@ -560,7 +586,7 @@ public class APopupMenu extends java.awt.PopupMenu implements if (dialog.accept) { EditCommand editCommand = new EditCommand(MessageManager.getString("label.edit_sequences"), - EditCommand.REPLACE, dialog.getName().replace(' ', + Action.REPLACE, dialog.getName().replace(' ', ap.av.getGapCharacter()), sg.getSequencesAsArray(ap.av.getHiddenRepSequences()), sg.getStartRes(), sg.getEndRes() + 1, @@ -742,11 +768,15 @@ public class APopupMenu extends java.awt.PopupMenu implements PDBEntry entry = (PDBEntry) seq.getPDBId().firstElement(); if (ap.av.applet.jmolAvailable) + { new jalview.appletgui.AppletJmol(entry, new Sequence[] { seq }, null, ap, AppletFormatAdapter.URL); + } else + { new MCview.AppletPDBViewer(entry, new Sequence[] { seq }, null, ap, AppletFormatAdapter.URL); + } } else diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 34ec2cf..ed64215 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -27,6 +27,7 @@ import jalview.api.SequenceStructureBinding; import jalview.bin.JalviewLite; import jalview.commands.CommandI; import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; import jalview.commands.OrderCommand; import jalview.commands.RemoveGapColCommand; import jalview.commands.RemoveGapsCommand; @@ -1833,7 +1834,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } // !newAlignment - addHistoryItem(new EditCommand(MessageManager.getString("label.add_sequences"), EditCommand.PASTE, + addHistoryItem(new EditCommand( + MessageManager.getString("label.add_sequences"), Action.PASTE, seqs, 0, viewport.getAlignment().getWidth(), viewport.getAlignment())); @@ -1883,8 +1885,9 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, /* * //ADD HISTORY ITEM */ - addHistoryItem(new EditCommand(MessageManager.getString("label.cut_sequences"), EditCommand.CUT, cut, - sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1, + addHistoryItem(new EditCommand( + MessageManager.getString("label.cut_sequences"), Action.CUT, + cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1, viewport.getAlignment())); viewport.setSelectionGroup(null); diff --git a/src/jalview/appletgui/RedundancyPanel.java b/src/jalview/appletgui/RedundancyPanel.java index 1547862..8e364f0 100644 --- a/src/jalview/appletgui/RedundancyPanel.java +++ b/src/jalview/appletgui/RedundancyPanel.java @@ -20,16 +20,26 @@ */ package jalview.appletgui; -import java.util.*; -import java.util.List; -import java.awt.*; -import java.awt.event.*; - import jalview.analysis.AlignSeq; -import jalview.commands.*; -import jalview.datamodel.*; +import jalview.commands.CommandI; +import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; import jalview.util.MessageManager; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +import java.util.Vector; + public class RedundancyPanel extends SliderPanel implements Runnable, WindowListener { @@ -197,7 +207,7 @@ public class RedundancyPanel extends SliderPanel implements Runnable, } EditCommand cut = new EditCommand(MessageManager.getString("action.remove_redundancy"), - EditCommand.CUT, deleted, 0, width, ap.av.getAlignment()); + Action.CUT, deleted, 0, width, ap.av.getAlignment()); AlignmentI alignment = ap.av.getAlignment(); for (int i = 0; i < del.size(); i++) { diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index de4d979..592fd4f 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -20,19 +20,31 @@ */ package jalview.appletgui; -import java.util.*; - -import java.awt.*; -import java.awt.event.*; - -import jalview.commands.*; -import jalview.datamodel.*; -import jalview.schemes.*; +import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SearchResults; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.schemes.ResidueProperties; import jalview.structure.SelectionSource; import jalview.structure.SequenceListener; import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Panel; +import java.awt.Point; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.Vector; + public class SeqPanel extends Panel implements MouseMotionListener, MouseListener, SequenceListener { @@ -147,7 +159,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, void setCursorPosition() { - SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt( + SequenceI sequence = av.getAlignment().getSequenceAt( seqCanvas.cursorY); seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1; @@ -240,7 +252,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, void setSelectionAreaAtCursor(boolean topLeft) { - SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt( + SequenceI sequence = av.getAlignment().getSequenceAt( seqCanvas.cursorY); if (av.getSelectionGroup() != null) @@ -648,7 +660,9 @@ public class SeqPanel extends Panel implements MouseMotionListener, { String tmp = sequence.hashCode() + index + ""; if (lastMessage == null || !lastMessage.equals(tmp)) + { ssm.mouseOverSequence(sequence, index, pos, av); + } lastMessage = tmp; } @@ -698,7 +712,9 @@ public class SeqPanel extends Panel implements MouseMotionListener, int respos = sequence.findPosition(res); if (ssm != null) + { mouseOverSequence(sequence, res, respos); + } StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " + sequence.getName()); @@ -813,7 +829,9 @@ public class SeqPanel extends Panel implements MouseMotionListener, .containsKey(features[i].featureGroup) && !((Boolean) seqCanvas.fr.featureGroups .get(features[i].featureGroup)).booleanValue()) + { continue; + } if ((features[i].getBegin() <= res) && (features[i].getEnd() >= res)) @@ -938,7 +956,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, { if (av.isHiddenRepSequence(seq)) { - sg = (SequenceGroup) av.getRepresentedSequences(seq); + sg = av.getRepresentedSequences(seq); groupEditing = true; } } @@ -1164,7 +1182,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } else { - editCommand.appendEdit(EditCommand.INSERT_GAP, groupSeqs, + editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres - lastres, av.getAlignment(), true); } } @@ -1180,7 +1198,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } else { - editCommand.appendEdit(EditCommand.DELETE_GAP, groupSeqs, + editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres - startres, av.getAlignment(), true); } @@ -1202,7 +1220,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } else { - editCommand.appendEdit(EditCommand.INSERT_GAP, new SequenceI[] + editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[] { seq }, lastres, startres - lastres, av.getAlignment(), true); } } @@ -1237,7 +1255,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, if (max > 0) { - editCommand.appendEdit(EditCommand.DELETE_GAP, new SequenceI[] + editCommand.appendEdit(Action.DELETE_GAP, new SequenceI[] { seq }, startres, max, av.getAlignment(), true); } } @@ -1273,10 +1291,10 @@ public class SeqPanel extends Panel implements MouseMotionListener, } } - editCommand.appendEdit(EditCommand.DELETE_GAP, seq, blankColumn, 1, + editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1, av.getAlignment(), true); - editCommand.appendEdit(EditCommand.INSERT_GAP, seq, j, 1, + editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(), true); } @@ -1284,10 +1302,10 @@ public class SeqPanel extends Panel implements MouseMotionListener, void deleteChar(int j, SequenceI[] seq, int fixedColumn) { - editCommand.appendEdit(EditCommand.DELETE_GAP, seq, j, 1, + editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(), true); - editCommand.appendEdit(EditCommand.INSERT_GAP, seq, fixedColumn, 1, + editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1, av.getAlignment(), true); } @@ -1312,7 +1330,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, return; } - SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq); + SequenceI sequence = av.getAlignment().getSequenceAt(seq); if (sequence == null || res > sequence.getLength()) { diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java index 5c698a8..82de3b2 100644 --- a/src/jalview/commands/EditCommand.java +++ b/src/jalview/commands/EditCommand.java @@ -27,8 +27,10 @@ import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import java.util.ArrayList; import java.util.Hashtable; import java.util.List; +import java.util.ListIterator; /** * @@ -54,19 +56,12 @@ import java.util.List; */ public class EditCommand implements CommandI { - public static final int INSERT_GAP = 0; - - public static final int DELETE_GAP = 1; - - public static final int CUT = 2; - - public static final int PASTE = 3; - - public static final int REPLACE = 4; - - public static final int INSERT_NUC = 5; + public enum Action + { + INSERT_GAP, DELETE_GAP, CUT, PASTE, REPLACE, INSERT_NUC + }; - Edit[] edits; + private List edits = new ArrayList(); String description; @@ -79,32 +74,75 @@ public class EditCommand implements CommandI this.description = description; } - public EditCommand(String description, int command, SequenceI[] seqs, + public EditCommand(String description, Action command, SequenceI[] seqs, int position, int number, AlignmentI al) { this.description = description; - if (command == CUT || command == PASTE) + if (command == Action.CUT || command == Action.PASTE) { - edits = new Edit[] - { new Edit(command, seqs, position, number, al) }; + setEdit(new Edit(command, seqs, position, number, al)); } performEdit(0, null); } - public EditCommand(String description, int command, String replace, + public EditCommand(String description, Action command, String replace, SequenceI[] seqs, int position, int number, AlignmentI al) { this.description = description; - if (command == REPLACE) + if (command == Action.REPLACE) { - edits = new Edit[] - { new Edit(command, seqs, position, number, al, replace) }; + setEdit(new Edit(command, seqs, position, number, al, replace)); } performEdit(0, null); } + /** + * Set the list of edits to the specified item (only). + * + * @param e + */ + protected void setEdit(Edit e) + { + edits.clear(); + edits.add(e); + } + + /** + * Add the given edit command to the stored list of commands. + * + * @param e + */ + protected void addEdit(Edit e) + { + edits.add(e); + } + + /** + * Clear the list of stored edit commands. + * + */ + protected void clearEdits() + { + edits.clear(); + } + + /** + * Returns the i'th stored Edit command. + * + * @param i + * @return + */ + protected Edit getEdit(int i) + { + if (i >= 0 && i < edits.size()) + { + return edits.get(i); + } + return null; + } + @Override final public String getDescription() { @@ -114,12 +152,17 @@ public class EditCommand implements CommandI @Override public int getSize() { - return edits == null ? 0 : edits.length; + return edits.size(); } + /** + * Return the alignment for the first edit (or null if no edit). + * + * @return + */ final public AlignmentI getAlignment() { - return edits[0].al; + return (edits.isEmpty() ? null : edits.get(0).al); } /** @@ -135,7 +178,8 @@ public class EditCommand implements CommandI * @param al * @param performEdit */ - final public void appendEdit(int command, SequenceI[] seqs, int position, + final public void appendEdit(Action command, SequenceI[] seqs, + int position, int number, AlignmentI al, boolean performEdit) { appendEdit(command, seqs, position, number, al, performEdit, null); @@ -153,7 +197,8 @@ public class EditCommand implements CommandI * @param performEdit * @param views */ - final public void appendEdit(int command, SequenceI[] seqs, int position, + final public void appendEdit(Action command, SequenceI[] seqs, + int position, int number, AlignmentI al, boolean performEdit, AlignmentI[] views) { Edit edit = new Edit(command, seqs, position, number, @@ -164,52 +209,62 @@ public class EditCommand implements CommandI edit.fullAlignmentHeight = true; } - if (edits != null) - { - Edit[] temp = new Edit[edits.length + 1]; - System.arraycopy(edits, 0, temp, 0, edits.length); - edits = temp; - edits[edits.length - 1] = edit; - } - else - { - edits = new Edit[] - { edit }; - } + edits.add(edit); if (performEdit) { - performEdit(edits.length - 1, views); + performEdit(edit, views); } } + /** + * Execute all the edit commands, starting at the given commandIndex + * + * @param commandIndex + * @param views + */ final void performEdit(int commandIndex, AlignmentI[] views) { - int eSize = edits.length; - for (int e = commandIndex; e < eSize; e++) + ListIterator iterator = edits.listIterator(commandIndex); + while (iterator.hasNext()) { - switch (edits[e].command) - { - case INSERT_GAP: - insertGap(edits[e]); - break; - case DELETE_GAP: - deleteGap(edits[e]); - break; - case CUT: - cut(edits[e], views); - break; - case PASTE: - paste(edits[e], views); - break; - case REPLACE: - replace(edits[e]); - break; + Edit edit = iterator.next(); + performEdit(edit, views); + } + } + + /** + * Execute one edit command in all the specified alignment views + * + * @param edit + * @param views + */ + protected void performEdit(Edit edit, AlignmentI[] views) + { + switch (edit.command) + { + case INSERT_GAP: + insertGap(edit); + break; + case DELETE_GAP: + deleteGap(edit); + break; + case CUT: + cut(edit, views); + break; + case PASTE: + paste(edit, views); + break; + case REPLACE: + replace(edit); + break; + case INSERT_NUC: // TODO:add deleteNuc for UNDO // case INSERT_NUC: // insertNuc(edits[e]); - // break; - } + break; + default: + break; } } @@ -219,32 +274,49 @@ public class EditCommand implements CommandI performEdit(0, views); } + /** + * Undo the stored list of commands, in reverse order. + */ @Override final public void undoCommand(AlignmentI[] views) { - for(Edit e : edits){ - switch (e.command) - { - case INSERT_GAP: - deleteGap(e); - break; - case DELETE_GAP: - insertGap(e); - break; - case CUT: - paste(e, views); - break; - case PASTE: - cut(e, views); - break; - case REPLACE: - replace(e); - break; - } + ListIterator iterator = edits.listIterator(edits.size()); + while (iterator.hasPrevious()) + { + Edit e = iterator.previous(); + switch (e.command) + { + case INSERT_GAP: + deleteGap(e); + break; + case DELETE_GAP: + insertGap(e); + break; + case CUT: + paste(e, views); + break; + case PASTE: + cut(e, views); + break; + case REPLACE: + replace(e); + break; + case INSERT_NUC: + // not implemented + break; + default: + break; + } } } - final void insertGap(Edit command) + /** + * Insert gap(s) in sequences as specified by the command, and adjust + * annotations. + * + * @param command + */ + final private void insertGap(Edit command) { for (int s = 0; s < command.seqs.length; s++) @@ -270,7 +342,13 @@ public class EditCommand implements CommandI // adjustAnnotations(command, true, false, null); // } - final void deleteGap(Edit command) + /** + * Delete gap(s) in sequences as specified by the command, and adjust + * annotations. + * + * @param command + */ + final private void deleteGap(Edit command) { for (int s = 0; s < command.seqs.length; s++) { @@ -281,6 +359,13 @@ public class EditCommand implements CommandI adjustAnnotations(command, false, false, null); } + /** + * Carry out a Cut action. The cut characters are saved in case Undo is + * requested. + * + * @param command + * @param views + */ void cut(Edit command, AlignmentI[] views) { boolean seqDeleted = false; @@ -288,29 +373,30 @@ public class EditCommand implements CommandI for (int i = 0; i < command.seqs.length; i++) { - if (command.seqs[i].getLength() > command.position) + final SequenceI sequence = command.seqs[i]; + if (sequence.getLength() > command.position) { - command.string[i] = command.seqs[i].getSequence(command.position, + command.string[i] = sequence.getSequence(command.position, command.position + command.number); - SequenceI oldds = command.seqs[i].getDatasetSequence(); + SequenceI oldds = sequence.getDatasetSequence(); if (command.oldds != null && command.oldds[i] != null) { // we are redoing an undone cut. - command.seqs[i].setDatasetSequence(null); + sequence.setDatasetSequence(null); } - command.seqs[i].deleteChars(command.position, command.position + sequence.deleteChars(command.position, command.position + command.number); if (command.oldds != null && command.oldds[i] != null) { // oldds entry contains the cut dataset sequence. - command.seqs[i].setDatasetSequence(command.oldds[i]); + sequence.setDatasetSequence(command.oldds[i]); command.oldds[i] = oldds; } else { // modify the oldds if necessary - if (oldds != command.seqs[i].getDatasetSequence() - || command.seqs[i].getSequenceFeatures() != null) + if (oldds != sequence.getDatasetSequence() + || sequence.getSequenceFeatures() != null) { if (command.oldds == null) { @@ -320,16 +406,16 @@ public class EditCommand implements CommandI adjustFeatures( command, i, - command.seqs[i].findPosition(command.position), - command.seqs[i].findPosition(command.position + sequence.findPosition(command.position), + sequence.findPosition(command.position + command.number), false); } } } - if (command.seqs[i].getLength() < 1) + if (sequence.getLength() < 1) { - command.al.deleteSequence(command.seqs[i]); + command.al.deleteSequence(sequence); seqDeleted = true; } } @@ -337,6 +423,13 @@ public class EditCommand implements CommandI adjustAnnotations(command, false, seqDeleted, views); } + /** + * Perform the given Paste command. This may be to add cut or copied sequences + * to an alignment, or to undo a 'Cut' action on a region of the alignment. + * + * @param command + * @param views + */ void paste(Edit command, AlignmentI[] views) { StringBuffer tmp; @@ -540,7 +633,7 @@ public class EditCommand implements CommandI if (modifyVisibility && !insert) { // only occurs if a sequence was added or deleted. - command.deletedAnnotationRows = new Hashtable(); + command.deletedAnnotationRows = new Hashtable(); } if (command.fullAlignmentHeight) { @@ -624,7 +717,7 @@ public class EditCommand implements CommandI && command.deletedAnnotationRows .containsKey(command.seqs[s])) { - AlignmentAnnotation[] revealed = (AlignmentAnnotation[]) command.deletedAnnotationRows + AlignmentAnnotation[] revealed = command.deletedAnnotationRows .get(command.seqs[s]); command.seqs[s].setAlignmentAnnotation(revealed); if (revealed != null) @@ -695,7 +788,7 @@ public class EditCommand implements CommandI if (!insert) { - command.deletedAnnotations = new Hashtable(); + command.deletedAnnotations = new Hashtable(); } int aSize; @@ -758,7 +851,7 @@ public class EditCommand implements CommandI && command.deletedAnnotations .containsKey(annotations[a].annotationId)) { - Annotation[] restore = (Annotation[]) command.deletedAnnotations + Annotation[] restore = command.deletedAnnotations .get(annotations[a].annotationId); System.arraycopy(restore, 0, temp, command.position, @@ -776,7 +869,7 @@ public class EditCommand implements CommandI && command.deletedAnnotations .containsKey(annotations[a].annotationId)) { - Annotation[] restore = (Annotation[]) command.deletedAnnotations + Annotation[] restore = command.deletedAnnotations .get(annotations[a].annotationId); temp = new Annotation[annotations[a].annotations.length @@ -871,7 +964,7 @@ public class EditCommand implements CommandI if (command.editedFeatures != null && command.editedFeatures.containsKey(seq)) { - sequence.setSequenceFeatures((SequenceFeature[]) command.editedFeatures + sequence.setSequenceFeatures(command.editedFeatures .get(seq)); } @@ -927,7 +1020,7 @@ public class EditCommand implements CommandI if (command.editedFeatures == null) { - command.editedFeatures = new Hashtable(); + command.editedFeatures = new Hashtable(); } command.editedFeatures.put(seq, oldsf); @@ -940,15 +1033,15 @@ public class EditCommand implements CommandI boolean fullAlignmentHeight = false; - Hashtable deletedAnnotationRows; + Hashtable deletedAnnotationRows; - Hashtable deletedAnnotations; + Hashtable deletedAnnotations; - Hashtable editedFeatures; + Hashtable editedFeatures; AlignmentI al; - int command; + Action command; char[][] string; @@ -960,7 +1053,7 @@ public class EditCommand implements CommandI char gapChar; - Edit(int command, SequenceI[] seqs, int position, int number, + Edit(Action command, SequenceI[] seqs, int position, int number, char gapChar) { this.command = command; @@ -970,7 +1063,7 @@ public class EditCommand implements CommandI this.gapChar = gapChar; } - Edit(int command, SequenceI[] seqs, int position, int number, + Edit(Action command, SequenceI[] seqs, int position, int number, AlignmentI al) { this.gapChar = al.getGapCharacter(); @@ -989,7 +1082,7 @@ public class EditCommand implements CommandI fullAlignmentHeight = (al.getHeight() == seqs.length); } - Edit(int command, SequenceI[] seqs, int position, int number, + Edit(Action command, SequenceI[] seqs, int position, int number, AlignmentI al, String replace) { this.command = command; diff --git a/src/jalview/commands/RemoveGapColCommand.java b/src/jalview/commands/RemoveGapColCommand.java index 6172ce8..ac93d4e 100644 --- a/src/jalview/commands/RemoveGapColCommand.java +++ b/src/jalview/commands/RemoveGapColCommand.java @@ -39,7 +39,8 @@ package jalview.commands; * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -import jalview.datamodel.*; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; public class RemoveGapColCommand extends EditCommand { @@ -55,7 +56,7 @@ public class RemoveGapColCommand extends EditCommand int startCol = -1, endCol = -1; columnsDeleted = 0; - edits = new Edit[0]; + clearEdits(); boolean delete = true; for (int i = start; i <= end; i++) @@ -86,7 +87,8 @@ public class RemoveGapColCommand extends EditCommand if (!delete && startCol > -1) { - this.appendEdit(DELETE_GAP, seqs, startCol - columnsDeleted, endCol + this.appendEdit(Action.DELETE_GAP, seqs, startCol - columnsDeleted, + endCol - startCol, al, false, null); columnsDeleted += (endCol - startCol); @@ -100,7 +102,8 @@ public class RemoveGapColCommand extends EditCommand // This is for empty columns at the // end of the alignment - this.appendEdit(DELETE_GAP, seqs, startCol - columnsDeleted, end + this.appendEdit(Action.DELETE_GAP, seqs, startCol - columnsDeleted, + end - startCol + 1, al, false, null); columnsDeleted += (end - startCol + 1); diff --git a/src/jalview/commands/RemoveGapsCommand.java b/src/jalview/commands/RemoveGapsCommand.java index 2e8d744..60d09f7 100644 --- a/src/jalview/commands/RemoveGapsCommand.java +++ b/src/jalview/commands/RemoveGapsCommand.java @@ -39,7 +39,8 @@ package jalview.commands; * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -import jalview.datamodel.*; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; public class RemoveGapsCommand extends EditCommand { @@ -74,7 +75,7 @@ public class RemoveGapsCommand extends EditCommand int j, jSize; - edits = new Edit[0]; + clearEdits(); boolean delete = true; char[] sequence; @@ -108,7 +109,7 @@ public class RemoveGapsCommand extends EditCommand if (!delete && startCol > -1) { - this.appendEdit(DELETE_GAP, new SequenceI[] + this.appendEdit(Action.DELETE_GAP, new SequenceI[] { seqs[s] }, start + startCol - deletedCols, endCol - startCol, al, false, null); @@ -119,7 +120,7 @@ public class RemoveGapsCommand extends EditCommand } if (delete && startCol > -1) { - this.appendEdit(DELETE_GAP, new SequenceI[] + this.appendEdit(Action.DELETE_GAP, new SequenceI[] { seqs[s] }, start + startCol - deletedCols, jSize - startCol, al, false, null); } diff --git a/src/jalview/commands/SlideSequencesCommand.java b/src/jalview/commands/SlideSequencesCommand.java index 1ad3d9e..382085c 100644 --- a/src/jalview/commands/SlideSequencesCommand.java +++ b/src/jalview/commands/SlideSequencesCommand.java @@ -20,7 +20,7 @@ */ package jalview.commands; -import jalview.datamodel.*; +import jalview.datamodel.SequenceI; public class SlideSequencesCommand extends EditCommand { @@ -37,21 +37,29 @@ public class SlideSequencesCommand extends EditCommand for (i = 0; i < lSize; i++) { for (j = 0; j < slideSize; j++) + { if (!jalview.util.Comparison.isGap(seqsLeft[i].getCharAt(j))) { gapsInsertedBegin = true; break; } + } } + Edit e = null; + if (!gapsInsertedBegin) - edits = new Edit[] - { new Edit(DELETE_GAP, seqsLeft, 0, slideSize, gapChar) }; + { + e = new Edit(Action.DELETE_GAP, seqsLeft, 0, slideSize, gapChar); + setEdit(e); + } else - edits = new Edit[] - { new Edit(INSERT_GAP, seqsRight, 0, slideSize, gapChar) }; + { + e = new Edit(Action.INSERT_GAP, seqsRight, 0, slideSize, gapChar); + setEdit(e); + } - performEdit(0, null); + performEdit(e, null); } public boolean getGapsInsertedBegin() @@ -63,12 +71,12 @@ public class SlideSequencesCommand extends EditCommand { boolean same = false; - if (command.edits[0].seqs.length == edits[0].seqs.length) + if (command.getEdit(0).seqs.length == getEdit(0).seqs.length) { same = true; - for (int i = 0; i < command.edits[0].seqs.length; i++) + for (int i = 0; i < command.getEdit(0).seqs.length; i++) { - if (edits[0].seqs[i] != command.edits[0].seqs[i]) + if (getEdit(0).seqs[i] != command.getEdit(0).seqs[i]) { same = false; } @@ -77,10 +85,7 @@ public class SlideSequencesCommand extends EditCommand if (same) { - Edit[] temp = new Edit[command.edits.length + 1]; - System.arraycopy(command.edits, 0, temp, 0, command.edits.length); - command.edits = temp; - command.edits[command.edits.length - 1] = edits[0]; + command.addEdit(getEdit(0)); } return same; diff --git a/src/jalview/commands/TrimRegionCommand.java b/src/jalview/commands/TrimRegionCommand.java index a757d1a..ebbe827 100644 --- a/src/jalview/commands/TrimRegionCommand.java +++ b/src/jalview/commands/TrimRegionCommand.java @@ -20,10 +20,13 @@ */ package jalview.commands; -import java.util.*; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.util.ShiftList; -import jalview.datamodel.*; -import jalview.util.*; +import java.util.List; public class TrimRegionCommand extends EditCommand { @@ -39,7 +42,7 @@ public class TrimRegionCommand extends EditCommand SequenceGroup selectionGroup; - Vector deletedHiddenColumns; + List deletedHiddenColumns; int columnsDeleted; @@ -59,8 +62,7 @@ public class TrimRegionCommand extends EditCommand columnsDeleted = column; - edits = new Edit[] - { new Edit(CUT, seqs, 0, column, al) }; + setEdit(new Edit(Action.CUT, seqs, 0, column, al)); } else if (command.equalsIgnoreCase(TRIM_RIGHT)) { @@ -72,17 +74,16 @@ public class TrimRegionCommand extends EditCommand columnsDeleted = width - 1; - edits = new Edit[] - { new Edit(CUT, seqs, column + 1, width, al) }; + setEdit(new Edit(Action.CUT, seqs, column + 1, width, al)); } // We need to keep a record of the sequence start // in order to restore the state after a redo - int i, isize = edits[0].seqs.length; + int i, isize = getEdit(0).seqs.length; start = new int[isize]; for (i = 0; i < isize; i++) { - start[i] = edits[0].seqs[i].getStart(); + start[i] = getEdit(0).seqs[i].getStart(); } performEdit(0, null); @@ -160,7 +161,7 @@ public class TrimRegionCommand extends EditCommand int[] region; for (int i = 0; i < deletedHiddenColumns.size(); i++) { - region = (int[]) deletedHiddenColumns.elementAt(i); + region = deletedHiddenColumns.get(i); colSel.hideColumns(region[0], region[1]); } } diff --git a/src/jalview/datamodel/ColumnSelection.java b/src/jalview/datamodel/ColumnSelection.java index 35d467a..f414d13 100644 --- a/src/jalview/datamodel/ColumnSelection.java +++ b/src/jalview/datamodel/ColumnSelection.java @@ -20,9 +20,12 @@ */ package jalview.datamodel; -import java.util.*; +import jalview.util.ShiftList; -import jalview.util.*; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Vector; /** * NOTE: Columns are zero based. @@ -186,9 +189,9 @@ public class ColumnSelection * @param left * shift in edit (+ve for removal, or -ve for inserts) */ - public Vector compensateForEdit(int start, int change) + public List compensateForEdit(int start, int change) { - Vector deletedHiddenColumns = null; + List deletedHiddenColumns = null; for (int i = 0; i < size(); i++) { int temp = columnAt(i); @@ -201,14 +204,14 @@ public class ColumnSelection if (hiddenColumns != null) { - deletedHiddenColumns = new Vector(); + deletedHiddenColumns = new ArrayList(); int hSize = hiddenColumns.size(); for (int i = 0; i < hSize; i++) { int[] region = (int[]) hiddenColumns.elementAt(i); if (region[0] > start && start + change > region[1]) { - deletedHiddenColumns.addElement(hiddenColumns.elementAt(i)); + deletedHiddenColumns.add(region); hiddenColumns.removeElementAt(i); i--; @@ -752,6 +755,7 @@ public class ColumnSelection public boolean isVisible(int column) { if (hiddenColumns != null) + { for (int i = 0; i < hiddenColumns.size(); i++) { int[] region = (int[]) hiddenColumns.elementAt(i); @@ -760,6 +764,7 @@ public class ColumnSelection return false; } } + } return true; } @@ -1018,7 +1023,9 @@ public class ColumnSelection w += els.length; } if (w == 0) + { return; + } Enumeration e = annels.elements(); alignmentAnnotation.annotations = new Annotation[w]; w = 0; diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 03a3944..07c4a54 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -34,6 +34,7 @@ import jalview.api.analysis.ScoreModelI; import jalview.bin.Cache; import jalview.commands.CommandI; import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; import jalview.commands.OrderCommand; import jalview.commands.RemoveGapColCommand; import jalview.commands.RemoveGapsCommand; @@ -1410,7 +1411,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (viewport.historyList.size() > 0) { undoMenuItem.setEnabled(true); - CommandI command = (CommandI) viewport.historyList.peek(); + CommandI command = viewport.historyList.peek(); undoMenuItem.setText(MessageManager.formatMessage( "label.undo_command", new String[] { command.getDescription() })); @@ -1425,7 +1426,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { redoMenuItem.setEnabled(true); - CommandI command = (CommandI) viewport.redoList.peek(); + CommandI command = viewport.redoList.peek(); redoMenuItem.setText(MessageManager.formatMessage( "label.redo_command", new String[] { command.getDescription() })); @@ -1489,7 +1490,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { return; } - CommandI command = (CommandI) viewport.historyList.pop(); + CommandI command = viewport.historyList.pop(); viewport.redoList.push(command); command.undoCommand(getViewAlignments()); @@ -1528,7 +1529,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, return; } - CommandI command = (CommandI) viewport.redoList.pop(); + CommandI command = viewport.redoList.pop(); viewport.historyList.push(command); command.doCommand(getViewAlignments()); @@ -1990,7 +1991,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // ///// // ADD HISTORY ITEM // - addHistoryItem(new EditCommand(MessageManager.getString("label.add_sequences"), EditCommand.PASTE, + addHistoryItem(new EditCommand( + MessageManager.getString("label.add_sequences"), + Action.PASTE, sequences, 0, alignment.getWidth(), alignment)); } // Add any annotations attached to sequences @@ -2261,8 +2264,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /* * //ADD HISTORY ITEM */ - addHistoryItem(new EditCommand(MessageManager.getString("label.cut_sequences"), EditCommand.CUT, cut, - sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1, + addHistoryItem(new EditCommand( + MessageManager.getString("label.cut_sequences"), Action.CUT, + cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1, viewport.getAlignment())); viewport.setSelectionGroup(null); diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 5c383d8..c5d19e2 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -42,6 +42,7 @@ import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.analysis.NJTree; import jalview.api.AlignViewportI; import jalview.bin.Cache; +import jalview.commands.CommandI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; @@ -140,9 +141,9 @@ public class AlignViewport extends AlignmentViewport implements boolean gatherViewsHere = false; - Stack historyList = new Stack(); + Stack historyList = new Stack(); - Stack redoList = new Stack(); + Stack redoList = new Stack(); int thresholdTextColour = 0; diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index dc26a36..9976471 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -25,6 +25,7 @@ import jalview.analysis.AlignmentAnnotationUtils; import jalview.analysis.Conservation; import jalview.commands.ChangeCaseCommand; import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.datamodel.DBRefEntry; @@ -2707,7 +2708,7 @@ public class PopupMenu extends JPopupMenu { EditCommand editCommand = new EditCommand( MessageManager.getString("label.edit_sequences"), - EditCommand.REPLACE, dialog.getName().replace(' ', + Action.REPLACE, dialog.getName().replace(' ', ap.av.getGapCharacter()), sg.getSequencesAsArray(ap.av.getHiddenRepSequences()), sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment()); diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java index 0e55cf8..ab0a0b8 100755 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@ -20,18 +20,28 @@ */ package jalview.gui; -import java.util.*; - -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.*; - import jalview.analysis.AlignSeq; -import jalview.commands.*; -import jalview.datamodel.*; -import jalview.jbgui.*; +import jalview.commands.CommandI; +import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.jbgui.GSliderPanel; import jalview.util.MessageManager; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +import java.util.Vector; + +import javax.swing.JInternalFrame; +import javax.swing.JProgressBar; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; + /** * DOCUMENT ME! * @@ -44,7 +54,9 @@ public class RedundancyPanel extends GSliderPanel implements Runnable AlignmentPanel ap; - Stack historyList = new Stack(); // simpler than synching with alignFrame. + Stack historyList = new Stack(); + + // simpler than synching with alignFrame. float[] redundancy; @@ -229,7 +241,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable } EditCommand cut = new EditCommand(MessageManager.getString("action.remove_redundancy"), - EditCommand.CUT, deleted, 0, width, ap.av.getAlignment()); + Action.CUT, deleted, 0, width, ap.av.getAlignment()); for (int i = 0; i < del.size(); i++) { @@ -263,7 +275,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable return; } - CommandI command = (CommandI) historyList.pop(); + CommandI command = historyList.pop(); if (ap.av.historyList.contains(command)) { command.undoCommand(af.getViewAlignments()); diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 61782b4..3b41620 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -21,6 +21,7 @@ package jalview.gui; import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SearchResults; import jalview.datamodel.Sequence; @@ -1190,7 +1191,7 @@ public class SeqPanel extends JPanel implements MouseListener, } else { - editCommand.appendEdit(EditCommand.INSERT_GAP, groupSeqs, + editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres - lastres, av.getAlignment(), true); } } @@ -1206,7 +1207,7 @@ public class SeqPanel extends JPanel implements MouseListener, } else { - editCommand.appendEdit(EditCommand.DELETE_GAP, groupSeqs, + editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres - startres, av.getAlignment(), true); } @@ -1228,7 +1229,7 @@ public class SeqPanel extends JPanel implements MouseListener, } else { - editCommand.appendEdit(EditCommand.INSERT_GAP, new SequenceI[] + editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[] { seq }, lastres, startres - lastres, av.getAlignment(), true); } } @@ -1265,7 +1266,7 @@ public class SeqPanel extends JPanel implements MouseListener, if (max > 0) { - editCommand.appendEdit(EditCommand.DELETE_GAP, + editCommand.appendEdit(Action.DELETE_GAP, new SequenceI[] { seq }, startres, max, av.getAlignment(), true); } @@ -1283,7 +1284,7 @@ public class SeqPanel extends JPanel implements MouseListener, } else { - editCommand.appendEdit(EditCommand.INSERT_NUC, new SequenceI[] + editCommand.appendEdit(Action.INSERT_NUC, new SequenceI[] { seq }, lastres, startres - lastres, av.getAlignment(), true); } } @@ -1319,10 +1320,10 @@ public class SeqPanel extends JPanel implements MouseListener, } } - editCommand.appendEdit(EditCommand.DELETE_GAP, seq, blankColumn, 1, + editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1, av.getAlignment(), true); - editCommand.appendEdit(EditCommand.INSERT_GAP, seq, j, 1, + editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(), true); } @@ -1330,10 +1331,10 @@ public class SeqPanel extends JPanel implements MouseListener, void deleteChar(int j, SequenceI[] seq, int fixedColumn) { - editCommand.appendEdit(EditCommand.DELETE_GAP, seq, j, 1, + editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(), true); - editCommand.appendEdit(EditCommand.INSERT_GAP, seq, fixedColumn, 1, + editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1, av.getAlignment(), true); } diff --git a/test/jalview/commands/EditCommandTest.java b/test/jalview/commands/EditCommandTest.java new file mode 100644 index 0000000..fc821b9 --- /dev/null +++ b/test/jalview/commands/EditCommandTest.java @@ -0,0 +1,232 @@ +package jalview.commands; + +import static org.junit.Assert.assertEquals; +import jalview.commands.EditCommand.Action; +import jalview.commands.EditCommand.Edit; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +/** + * Unit tests for EditCommand + * + * @author gmcarstairs + * + */ +public class EditCommandTest +{ + + private EditCommand testee; + + private SequenceI[] seqs; + + private Alignment al; + + @Before + public void setUp() + { + testee = new EditCommand(); + seqs = new SequenceI[4]; + seqs[0] = new Sequence("seq0", "abcdefghjk"); + seqs[1] = new Sequence("seq1", "fghjklmnopq"); + seqs[2] = new Sequence("seq2", "qrstuvwxyz"); + seqs[3] = new Sequence("seq3", "1234567890"); + al = new Alignment(seqs); + al.setGapCharacter('?'); + } + + /** + * Test inserting gap characters + */ + @Test + public void testAppendEdit_insertGap() + { + // set a non-standard gap character to prove it is actually used + testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true); + assertEquals("abcd???efghjk", seqs[0].getSequenceAsString()); + assertEquals("fghj???klmnopq", seqs[1].getSequenceAsString()); + assertEquals("qrst???uvwxyz", seqs[2].getSequenceAsString()); + assertEquals("1234???567890", seqs[3].getSequenceAsString()); + + // todo: test for handling out of range positions? + } + + /** + * Test deleting characters from sequences. Note the deleteGap() action does + * not check that only gap characters are being removed. + */ + @Test + public void testAppendEdit_deleteGap() + { + testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true); + assertEquals("abcdhjk", seqs[0].getSequenceAsString()); + assertEquals("fghjnopq", seqs[1].getSequenceAsString()); + assertEquals("qrstxyz", seqs[2].getSequenceAsString()); + assertEquals("1234890", seqs[3].getSequenceAsString()); + } + + /** + * Test a cut action. The command should store the cut characters to support + * undo. + */ + @Test + public void testCut() + { + Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al); + testee.cut(ec, new AlignmentI[] + { al }); + assertEquals("abcdhjk", seqs[0].getSequenceAsString()); + assertEquals("fghjnopq", seqs[1].getSequenceAsString()); + assertEquals("qrstxyz", seqs[2].getSequenceAsString()); + assertEquals("1234890", seqs[3].getSequenceAsString()); + + assertEquals("efg", new String(ec.string[0])); + assertEquals("klm", new String(ec.string[1])); + assertEquals("uvw", new String(ec.string[2])); + assertEquals("567", new String(ec.string[3])); + // TODO: case where whole sequence is deleted as nothing left; etc + } + + /** + * Test a Paste action, where this adds sequences to an alignment. + */ + @Test + @Ignore + // TODO fix so it works + public void testPaste_addToAlignment() + { + SequenceI[] newSeqs = new SequenceI[2]; + newSeqs[0] = new Sequence("newseq0", "ACEFKL"); + newSeqs[1] = new Sequence("newseq1", "JWMPDH"); + + Edit ec = testee.new Edit(Action.PASTE, newSeqs, 0, al.getWidth(), al); + testee.paste(ec, new AlignmentI[] + { al }); + assertEquals(6, al.getSequences().size()); + assertEquals("1234567890", seqs[3].getSequenceAsString()); + assertEquals("ACEFKL", seqs[4].getSequenceAsString()); + assertEquals("JWMPDH", seqs[5].getSequenceAsString()); + } + + /** + * Test insertGap followed by undo command + */ + @Test + public void testUndo_insertGap() + { + // Edit ec = testee.new Edit(Action.INSERT_GAP, seqs, 4, 3, '?'); + testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true); + // check something changed + assertEquals("abcd???efghjk", seqs[0].getSequenceAsString()); + testee.undoCommand(new AlignmentI[] + { al }); + assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); + assertEquals("fghjklmnopq", seqs[1].getSequenceAsString()); + assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString()); + assertEquals("1234567890", seqs[3].getSequenceAsString()); + } + + /** + * Test deleteGap followed by undo command + */ + @Test + public void testUndo_deleteGap() + { + testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true); + // check something changed + assertEquals("abcdhjk", seqs[0].getSequenceAsString()); + testee.undoCommand(new AlignmentI[] + { al }); + // deleteGap doesn't 'remember' deleted characters, only gaps get put back + assertEquals("abcd???hjk", seqs[0].getSequenceAsString()); + assertEquals("fghj???nopq", seqs[1].getSequenceAsString()); + assertEquals("qrst???xyz", seqs[2].getSequenceAsString()); + assertEquals("1234???890", seqs[3].getSequenceAsString()); + } + + /** + * Test several commands followed by an undo command + */ + @Test + public void testUndo_multipleCommands() + { + // delete positions 3/4/5 (counting from 1) + testee.appendEdit(Action.DELETE_GAP, seqs, 2, 3, al, true); + assertEquals("abfghjk", seqs[0].getSequenceAsString()); + assertEquals("1267890", seqs[3].getSequenceAsString()); + + // insert 2 gaps after the second residue + testee.appendEdit(Action.INSERT_GAP, seqs, 2, 2, al, true); + assertEquals("ab??fghjk", seqs[0].getSequenceAsString()); + assertEquals("12??67890", seqs[3].getSequenceAsString()); + + // delete positions 4/5/6 + testee.appendEdit(Action.DELETE_GAP, seqs, 3, 3, al, true); + assertEquals("ab?hjk", seqs[0].getSequenceAsString()); + assertEquals("12?890", seqs[3].getSequenceAsString()); + + // undo edit commands + testee.undoCommand(new AlignmentI[] + { al }); + assertEquals("ab?????hjk", seqs[0].getSequenceAsString()); + assertEquals("12?????890", seqs[3].getSequenceAsString()); + } + + /** + * Unit test for JAL-1594 bug: click and drag sequence right to insert gaps - + * undo did not remove them all. + */ + @Test + public void testUndo_multipleInsertGaps() + { + testee.appendEdit(Action.INSERT_GAP, seqs, 4, 1, al, true); + testee.appendEdit(Action.INSERT_GAP, seqs, 5, 1, al, true); + testee.appendEdit(Action.INSERT_GAP, seqs, 6, 1, al, true); + + // undo edit commands + testee.undoCommand(new AlignmentI[] + { al }); + assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); + assertEquals("1234567890", seqs[3].getSequenceAsString()); + + } + + /** + * Test cut followed by undo command + */ + @Test + public void testUndo_cut() + { + testee.appendEdit(Action.CUT, seqs, 4, 3, al, true); + // check something changed + assertEquals("abcdhjk", seqs[0].getSequenceAsString()); + testee.undoCommand(new AlignmentI[] + { al }); + assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); + assertEquals("fghjklmnopq", seqs[1].getSequenceAsString()); + assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString()); + assertEquals("1234567890", seqs[3].getSequenceAsString()); + } + + /** + * Test the replace command (used to manually edit a sequence) + */ + @Test + public void testReplace() + { + // seem to need a dataset sequence on the edited sequence here + seqs[1].setDatasetSequence(seqs[1]); + new EditCommand("", Action.REPLACE, "ZXY", new SequenceI[] + { seqs[1] }, 4, 8, al); + assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); + assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString()); + assertEquals("1234567890", seqs[3].getSequenceAsString()); + seqs[1] = new Sequence("seq1", "fghjZXYnopq"); + + } +} -- 1.7.10.2