X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fcommands%2FEditCommand.java;h=c15bbf8f7981c69b784a264a9132b02bd07f6584;hb=cd669a0e8c7b91b379bca8fe6e702cf0fcbd1ce0;hp=06f59cbadf84719ea5d4943ec745a0700c00e48d;hpb=c29cd7be43dcd8575d1b4b1e53920117af706ca4;p=jalview.git diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java index 06f59cb..c15bbf8 100644 --- a/src/jalview/commands/EditCommand.java +++ b/src/jalview/commands/EditCommand.java @@ -20,10 +20,13 @@ */ package jalview.commands; +import java.util.Locale; + import jalview.analysis.AlignSeq; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.ContiguousI; import jalview.datamodel.Range; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; @@ -115,10 +118,11 @@ public class EditCommand implements CommandI return null; } }; + public abstract Action getUndoAction(); }; - private List edits = new ArrayList(); + private List edits = new ArrayList<>(); String description; @@ -469,7 +473,7 @@ public class EditCommand implements CommandI { command.seqs[s].insertCharAt(command.position, command.number, command.gapChar); - // System.out.println("pos: "+command.position+" number: + // jalview.bin.Console.outPrintln("pos: "+command.position+" number: // "+command.number); } @@ -482,7 +486,7 @@ public class EditCommand implements CommandI // // for (int s = 0; s < command.seqs.length; s++) // { - // System.out.println("pos: "+command.position+" number: "+command.number); + // jalview.bin.Console.outPrintln("pos: "+command.position+" number: "+command.number); // command.seqs[s].insertCharAt(command.position, command.number,'A'); // } // @@ -526,45 +530,36 @@ public class EditCommand implements CommandI command.string[i] = sequence.getSequence(command.position, command.position + command.number); SequenceI oldds = sequence.getDatasetSequence(); - if (command.oldds != null && command.oldds[i] != null) - { - // we are redoing an undone cut. - sequence.setDatasetSequence(null); - } - Range cutPositions = sequence.findPositions(command.position + 1, - command.position + command.number); + ContiguousI cutPositions = sequence.findPositions( + command.position + 1, command.position + command.number); boolean cutIsInternal = cutPositions != null - && sequence.getStart() != cutPositions - .getBegin() && sequence.getEnd() != cutPositions.getEnd(); + && sequence.getStart() != cutPositions.getBegin() + && sequence.getEnd() != cutPositions.getEnd(); /* * perform the cut; if this results in a new dataset sequence, add * that to the alignment dataset */ SequenceI ds = sequence.getDatasetSequence(); - sequence.deleteChars(command.position, command.position - + command.number); - SequenceI newDs = sequence.getDatasetSequence(); - if (newDs != ds && command.al != null - && command.al.getDataset() != null - && !command.al.getDataset().getSequences().contains(newDs)) - { - command.al.getDataset().addSequence(newDs); - } + sequence.deleteChars(command.position, + command.position + command.number); if (command.oldds != null && command.oldds[i] != null) { - // Undoing previous Paste - so - // oldds entry contains the cut dataset sequence, - // with sequence features in expected place. + /* + * we are Redoing a Cut, or Undoing a Paste - so + * oldds entry contains the cut dataset sequence, + * with sequence features in expected place + */ sequence.setDatasetSequence(command.oldds[i]); command.oldds[i] = oldds; } else { - // New cut operation - // We always keep track of the dataset sequence so we can safely - // restore it during the Undo + /* + * new cut operation: save the dataset sequence + * so it can be restored in an Undo + */ if (command.oldds == null) { command.oldds = new SequenceI[command.seqs.length]; @@ -572,18 +567,24 @@ public class EditCommand implements CommandI command.oldds[i] = oldds;// todo not if !cutIsInternal? // do we need to edit sequence features for new sequence ? - if (oldds != sequence.getDatasetSequence() - || (cutIsInternal - && sequence.getFeatures().hasFeatures())) + if (oldds != sequence.getDatasetSequence() || (cutIsInternal + && sequence.getFeatures().hasFeatures())) // todo or just test cutIsInternal && cutPositions != null ? { if (cutPositions != null) { cutFeatures(command, sequence, cutPositions.getBegin(), - cutPositions.getEnd(), cutIsInternal); + cutPositions.getEnd(), cutIsInternal); } } } + SequenceI newDs = sequence.getDatasetSequence(); + if (newDs != ds && command.al != null + && command.al.getDataset() != null + && !command.al.getDataset().getSequences().contains(newDs)) + { + command.al.getDataset().addSequence(newDs); + } } if (sequence.getLength() < 1) @@ -620,8 +621,8 @@ public class EditCommand implements CommandI */ if (command.alIndex[i] < command.al.getHeight()) { - List sequences; - synchronized (sequences = command.al.getSequences()) + List sequences = command.al.getSequences(); + synchronized (sequences) { if (!(command.alIndex[i] < 0)) { @@ -720,6 +721,12 @@ public class EditCommand implements CommandI command.oldds[i] = sequence.getDatasetSequence(); sameDatasetSequence = ds == sequence.getDatasetSequence(); ds.setSequenceFeatures(sequence.getSequenceFeatures()); + if (!sameDatasetSequence && command.al.getDataset() != null) + { + // delete 'undone' sequence from alignment dataset + command.al.getDataset() + .deleteSequence(sequence.getDatasetSequence()); + } sequence.setDatasetSequence(ds); } undoCutFeatures(command, command.seqs[i], start, length, @@ -733,7 +740,7 @@ public class EditCommand implements CommandI static void replace(Edit command) { - StringBuffer tmp; + StringBuilder tmp; String oldstring; int start = command.position; int end = command.number; @@ -746,7 +753,8 @@ public class EditCommand implements CommandI { boolean newDSWasNeeded = command.oldds != null && command.oldds[i] != null; - boolean newStartEndWasNeeded = command.oldStartEnd!=null && command.oldStartEnd[i]!=null; + boolean newStartEndWasNeeded = command.oldStartEnd != null + && command.oldStartEnd[i] != null; /** * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, @@ -759,23 +767,24 @@ public class EditCommand implements CommandI * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) ); * */ + ContiguousI beforeEditedPositions = command.seqs[i].findPositions(1, + start); + ContiguousI afterEditedPositions = command.seqs[i] + .findPositions(end + 1, command.seqs[i].getLength()); - Range beforeEditedPositions = command.seqs[i].findPositions(1, start); - Range afterEditedPositions = command.seqs[i] - .findPositions(start + end + 1, command.seqs[i].getLength()); - oldstring = command.seqs[i].getSequenceAsString(); - tmp = new StringBuffer(oldstring.substring(0, start)); + tmp = new StringBuilder(oldstring.substring(0, start)); tmp.append(command.string[i]); - String nogaprep = jalview.analysis.AlignSeq.extractGaps( - jalview.util.Comparison.GapChars, + String nogaprep = AlignSeq.extractGaps(Comparison.GapChars, new String(command.string[i])); - int ipos = command.seqs[i].findPosition(start) - - command.seqs[i].getStart(); if (end < oldstring.length()) { tmp.append(oldstring.substring(end)); } + // stash end prior to updating the sequence object so we can save it if + // need be. + Range oldstartend = new Range(command.seqs[i].getStart(), + command.seqs[i].getEnd()); command.seqs[i].setSequence(tmp.toString()); command.string[i] = oldstring .substring(start, Math.min(end, oldstring.length())) @@ -783,33 +792,45 @@ public class EditCommand implements CommandI String nogapold = AlignSeq.extractGaps(Comparison.GapChars, new String(command.string[i])); - if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase())) + if (!nogaprep.toLowerCase(Locale.ROOT) + .equals(nogapold.toLowerCase(Locale.ROOT))) { - // probably need a new dataset sequence - if (newDSWasNeeded) + // we may already have dataset and limits stashed... + if (newDSWasNeeded || newStartEndWasNeeded) { - // then just switch the dataset sequence - SequenceI oldds = command.seqs[i].getDatasetSequence(); - command.seqs[i].setDatasetSequence(command.oldds[i]); - command.oldds[i] = oldds; + if (newDSWasNeeded) + { + // then just switch the dataset sequence + SequenceI oldds = command.seqs[i].getDatasetSequence(); + command.seqs[i].setDatasetSequence(command.oldds[i]); + command.oldds[i] = oldds; + } + if (newStartEndWasNeeded) + { + Range newStart = command.oldStartEnd[i]; + command.oldStartEnd[i] = oldstartend; + command.seqs[i].setStart(newStart.getBegin()); + command.seqs[i].setEnd(newStart.getEnd()); + } } else - if (newStartEndWasNeeded) - { - Range newStart = command.oldStartEnd[i]; - command.oldStartEnd[i] = new Range(command.seqs[i].getStart(), - command.seqs[i].getEnd()); - command.seqs[i].setStart(newStart.getBegin()); - command.seqs[i].setEnd(newStart.getEnd()); - } - else { + // decide if we need a new dataset sequence or modify start/end // first edit the original dataset sequence string SequenceI oldds = command.seqs[i].getDatasetSequence(); String osp = oldds.getSequenceAsString(); - - String fullseq = osp.substring(0, ipos) + nogaprep - + osp.substring(ipos + nogaprep.length()); + int beforeStartOfEdit = -oldds.getStart() + 1 + + (beforeEditedPositions == null + ? ((afterEditedPositions != null) + ? afterEditedPositions.getBegin() - 1 + : oldstartend.getBegin() + + nogapold.length()) + : beforeEditedPositions.getEnd()); + int afterEndOfEdit = -oldds.getStart() + 1 + + ((afterEditedPositions == null) ? oldstartend.getEnd() + : afterEditedPositions.getBegin() - 1); + String fullseq = osp.substring(0, beforeStartOfEdit) + nogaprep + + osp.substring(afterEndOfEdit); // and check if new sequence data is different.. if (!fullseq.equalsIgnoreCase(osp)) @@ -817,13 +838,21 @@ public class EditCommand implements CommandI // old ds and edited ds are different, so // create the new dataset sequence SequenceI newds = new Sequence(oldds); - newds.setSequence(fullseq.toUpperCase()); + newds.setSequence(fullseq.toUpperCase(Locale.ROOT)); if (command.oldds == null) { command.oldds = new SequenceI[command.seqs.length]; } command.oldds[i] = command.seqs[i].getDatasetSequence(); + + // And preserve start/end for good-measure + + if (command.oldStartEnd == null) + { + command.oldStartEnd = new Range[command.seqs.length]; + } + command.oldStartEnd[i] = oldstartend; // TODO: JAL-1131 ensure newly created dataset sequence is added to // the set of // dataset sequences associated with the alignment. @@ -845,8 +874,8 @@ public class EditCommand implements CommandI && afterEditedPositions == null) { // modification at end - command.seqs[i].setEnd( - beforeEditedPositions.getEnd() + nogaprep.length()); + command.seqs[i].setEnd(beforeEditedPositions.getEnd() + + nogaprep.length() - nogapold.length()); } else if (afterEditedPositions != null && beforeEditedPositions == null) @@ -860,9 +889,9 @@ public class EditCommand implements CommandI // edit covered both start and end. Here we can only guess the // new // start/end - String nogapalseq = jalview.analysis.AlignSeq.extractGaps( - jalview.util.Comparison.GapChars, - command.seqs[i].getSequenceAsString().toUpperCase()); + String nogapalseq = AlignSeq.extractGaps(Comparison.GapChars, + command.seqs[i].getSequenceAsString() + .toUpperCase(Locale.ROOT)); int newStart = command.seqs[i].getDatasetSequence() .getSequenceAsString().indexOf(nogapalseq); if (newStart == -1) @@ -891,7 +920,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) { @@ -994,7 +1023,8 @@ public class EditCommand implements CommandI } // and then duplicate added annotation on every other alignment // view - for (int vnum = 0; views != null && vnum < views.length; vnum++) + for (int vnum = 0; views != null + && vnum < views.length; vnum++) { if (views[vnum] != command.al) { @@ -1049,7 +1079,7 @@ public class EditCommand implements CommandI if (!insert) { - command.deletedAnnotations = new Hashtable(); + command.deletedAnnotations = new Hashtable<>(); } int aSize; @@ -1268,9 +1298,9 @@ public class EditCommand implements CommandI * feature was shifted left to cut position (not truncated), * so shift it back right */ - SequenceFeature shifted = new SequenceFeature(sf, sf.getBegin() - + length, sf.getEnd() + length, sf.getFeatureGroup(), - sf.getScore()); + SequenceFeature shifted = new SequenceFeature(sf, + sf.getBegin() + length, sf.getEnd() + length, + sf.getFeatureGroup(), sf.getScore()); seq.addSequenceFeature(shifted); seq.deleteFeature(sf); } @@ -1333,7 +1363,7 @@ public class EditCommand implements CommandI */ public Map priorState(boolean forUndo) { - Map result = new HashMap(); + Map result = new HashMap<>(); if (getEdits() == null) { return result; @@ -1366,7 +1396,7 @@ public class EditCommand implements CommandI * Work backwards through the edit list, deriving the sequences before each * was applied. The final result is the sequence set before any edits. */ - Iterator editList = new ReverseListIterator(getEdits()); + Iterator editList = new ReverseListIterator<>(getEdits()); while (editList.hasNext()) { Edit oldEdit = editList.next(); @@ -1403,7 +1433,7 @@ public class EditCommand implements CommandI } else { - System.err.println("Can't undo edit action " + action); + jalview.bin.Console.errPrintln("Can't undo edit action " + action); // throw new IllegalStateException("Can't undo edit action " + // action); } @@ -1415,48 +1445,53 @@ public class EditCommand implements CommandI public class Edit { - private SequenceI[] oldds; + SequenceI[] oldds; /** * start and end of sequence prior to edit */ - private Range[] oldStartEnd; + Range[] oldStartEnd; - private boolean fullAlignmentHeight = false; + boolean fullAlignmentHeight = false; - private Map deletedAnnotationRows; + Map deletedAnnotationRows; - private Map deletedAnnotations; + Map deletedAnnotations; /* * features deleted by the cut (re-add on Undo) * (including the original of any shortened features) */ - private Map> deletedFeatures; + Map> deletedFeatures; /* * shortened features added by the cut (delete on Undo) */ - private Map> truncatedFeatures; + Map> truncatedFeatures; - private AlignmentI al; + AlignmentI al; - final private Action command; + final Action command; char[][] string; SequenceI[] seqs; - private int[] alIndex; + int[] alIndex; - private int position; + int position; - private int number; + int number; - private char gapChar; + char gapChar; - public Edit(Action cmd, SequenceI[] sqs, int pos, int count, - char gap) + /* + * flag that identifies edits inserted to balance + * user edits in a 'locked editing' region + */ + private boolean systemGenerated; + + public Edit(Action cmd, SequenceI[] sqs, int pos, int count, char gap) { this.command = cmd; this.seqs = sqs; @@ -1465,8 +1500,7 @@ public class EditCommand implements CommandI this.gapChar = gap; } - Edit(Action cmd, SequenceI[] sqs, int pos, int count, - AlignmentI align) + Edit(Action cmd, SequenceI[] sqs, int pos, int count, AlignmentI align) { this(cmd, sqs, pos, count, align.getGapCharacter()); @@ -1491,8 +1525,8 @@ public class EditCommand implements CommandI * @param align * @param replace */ - Edit(Action cmd, SequenceI[] sqs, int pos, int count, - AlignmentI align, String replace) + Edit(Action cmd, SequenceI[] sqs, int pos, int count, AlignmentI align, + String replace) { this(cmd, sqs, pos, count, align); @@ -1527,6 +1561,16 @@ public class EditCommand implements CommandI { return gapChar; } + + public void setSystemGenerated(boolean b) + { + systemGenerated = b; + } + + public boolean isSystemGenerated() + { + return systemGenerated; + } } /** @@ -1544,7 +1588,7 @@ public class EditCommand implements CommandI } else { - return new ReverseListIterator(getEdits()); + return new ReverseListIterator<>(getEdits()); } } @@ -1579,25 +1623,25 @@ public class EditCommand implements CommandI } List added = new ArrayList<>(); List removed = new ArrayList<>(); - + SequenceFeaturesI featureStore = seq.getFeatures(); if (toPosition < fromPosition || featureStore == null) { return; } - + int cutStartPos = fromPosition; int cutEndPos = toPosition; int cutWidth = cutEndPos - cutStartPos + 1; - + synchronized (featureStore) { /* * get features that overlap the cut region */ - List toAmend = featureStore.findFeatures( - cutStartPos, cutEndPos); - + List toAmend = featureStore.findFeatures(cutStartPos, + cutEndPos); + /* * add any contact features that span the cut region * (not returned by findFeatures) @@ -1624,7 +1668,7 @@ public class EditCommand implements CommandI int newEnd = sfEnd; boolean toDelete = false; boolean follows = false; - + if (sfBegin >= cutStartPos && sfEnd <= cutEndPos) { /* @@ -1663,7 +1707,7 @@ public class EditCommand implements CommandI toDelete = true; } } - + seq.deleteFeature(sf); if (!follows) { @@ -1680,7 +1724,7 @@ public class EditCommand implements CommandI } } } - + /* * and left shift any features lying to the right of the cut region */