From 207a67738b6893c9f83e562b1eda83ec27f2b2d0 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 20 Jun 2017 10:33:28 +0100 Subject: [PATCH] JAL-2541 working Cut/Undo with feature relocation Conflicts: src/jalview/commands/EditCommand.java src/jalview/datamodel/Sequence.java src/jalview/datamodel/SequenceI.java test/jalview/datamodel/SequenceTest.java --- src/jalview/commands/EditCommand.java | 323 ++++++++++++++++------------ src/jalview/datamodel/Sequence.java | 84 +------- src/jalview/datamodel/SequenceI.java | 18 -- test/jalview/commands/EditCommandTest.java | 79 +++---- test/jalview/datamodel/SequenceTest.java | 124 ++++++----- 5 files changed, 310 insertions(+), 318 deletions(-) diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java index 5248dc9..c9b1e1f 100644 --- a/src/jalview/commands/EditCommand.java +++ b/src/jalview/commands/EditCommand.java @@ -24,9 +24,11 @@ import jalview.analysis.AlignSeq; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.Range; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.datamodel.features.SequenceFeaturesI; import jalview.util.Comparison; import jalview.util.ReverseListIterator; import jalview.util.StringUtils; @@ -332,20 +334,8 @@ public class EditCommand implements CommandI int position, int number, AlignmentI al, boolean performEdit, AlignmentI[] views) { - Edit edit = new Edit(command, seqs, position, number, - al.getGapCharacter()); - if (al.getHeight() == seqs.length) - { - edit.al = al; - edit.fullAlignmentHeight = true; - } - - addEdit(edit); - - if (performEdit) - { - performEdit(edit, views); - } + Edit edit = new Edit(command, seqs, position, number, al); + appendEdit(edit, al, performEdit, views); } /** @@ -541,8 +531,14 @@ public class EditCommand implements CommandI // we are redoing an undone cut. sequence.setDatasetSequence(null); } - sequence.deleteChars(command.position, + Range cutPositions = sequence.findPositions(command.position + 1, command.position + command.number); + boolean cutIsInternal = cutPositions != null + && sequence.getStart() != cutPositions + .getBegin() && sequence.getEnd() != cutPositions.getEnd(); + sequence.deleteChars(command.position, command.position + + command.number); + if (command.oldds != null && command.oldds[i] != null) { // oldds entry contains the cut dataset sequence. @@ -560,22 +556,12 @@ public class EditCommand implements CommandI command.oldds = new SequenceI[command.seqs.length]; } command.oldds[i] = oldds; - // FIXME JAL-2541 JAL-2526 get correct positions if on a gap - List amendedFeatures = sequence - .adjustFeatures(command.position, command.position - + command.number - 1); - if (command.editedFeatures == null) + + if (cutPositions != null) { - command.editedFeatures = new HashMap<>(); + cutFeatures(command, sequence, cutPositions.getBegin(), + cutPositions.getEnd(), cutIsInternal); } - command.editedFeatures.put(sequence, amendedFeatures); - // - // adjustFeatures( - // command, - // i, - // sequence.findPosition(command.position), - // sequence.findPosition(command.position + command.number), - // false); } } } @@ -656,8 +642,6 @@ public class EditCommand implements CommandI tmp.insert(command.position, command.string[i]); for (int s = 0; s < command.string[i].length; s++) { - // if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] - // != 23) if (!Comparison.isGap(command.string[i][s])) { if (!newDSNeeded) @@ -1133,101 +1117,34 @@ public class EditCommand implements CommandI } /* - * insert == true for an Undo of a Cut; restore the original features - * and delete the amended ones + * TODO: shift right features that lie to the right of the restored cut + * Currently not needed as all features restored with saved dataset sequence + * nor if no saved dataset sequence (as coordinates left unchanged by Cut) */ - if (true) - { - // TODO shift right features that lie to the right of the restored cut - // (add a start position parameter to SequenceFeatures.shift) - if (command.editedFeatures != null - && command.editedFeatures.containsKey(seq)) + /* + * restore any features that were deleted or truncated + */ + if (command.deletedFeatures != null + && command.deletedFeatures.containsKey(seq)) + { + for (SequenceFeature deleted : command.deletedFeatures.get(seq)) { - for (SequenceFeature[] toRestore : command.editedFeatures.get(seq)) - { - sequence.addSequenceFeature(toRestore[0]); - if (toRestore[1] != null) - { - sequence.deleteFeature(toRestore[1]); - } - } + sequence.addSequenceFeature(deleted); } - return; } - // List sf = sequence.getFeatures() - // .getPositionalFeatures(); - // - // if (sf.isEmpty()) - // { - // return; - // } - // - // List oldsf = new ArrayList(); - // - // int cSize = j - i; - // - // for (SequenceFeature feature : sf) - // { - // SequenceFeature copy = new SequenceFeature(feature); - // - // oldsf.add(copy); - // - // if (feature.getEnd() < i) - // { - // continue; - // } - // - // if (feature.getBegin() > j) - // { - // int newBegin = copy.getBegin() - cSize; - // int newEnd = copy.getEnd() - cSize; - // SequenceFeature newSf = new SequenceFeature(feature, newBegin, - // newEnd, feature.getFeatureGroup(), feature.getScore()); - // sequence.deleteFeature(feature); - // sequence.addSequenceFeature(newSf); - // // feature.setBegin(newBegin); - // // feature.setEnd(newEnd); - // continue; - // } - // - // int newBegin = feature.getBegin(); - // int newEnd = feature.getEnd(); - // if (newBegin >= i) - // { - // newBegin = i; - // // feature.setBegin(i); - // } - // - // if (newEnd < j) - // { - // newEnd = j - 1; - // // feature.setEnd(j - 1); - // } - // newEnd = newEnd - cSize; - // // feature.setEnd(feature.getEnd() - (cSize)); - // - // sequence.deleteFeature(feature); - // if (newEnd >= newBegin) - // { - // sequence.addSequenceFeature(new SequenceFeature(feature, newBegin, - // newEnd, feature.getFeatureGroup(), feature.getScore())); - // } - // // if (feature.getBegin() > feature.getEnd()) - // // { - // // sequence.deleteFeature(feature); - // // } - // } - // - // if (command.editedFeatures == null) - // { - // command.editedFeatures = new Hashtable>(); - // } - // - // command.editedFeatures.put(seq, oldsf); - + /* + * delete any truncated features + */ + if (command.truncatedFeatures != null + && command.truncatedFeatures.containsKey(seq)) + { + for (SequenceFeature amended : command.truncatedFeatures.get(seq)) + { + sequence.deleteFeature(amended); + } + } } /** @@ -1350,7 +1267,15 @@ public class EditCommand implements CommandI Map deletedAnnotations; - Map> editedFeatures; + /* + * features deleted by the cut (re-add on Undo) + */ + Map> deletedFeatures; + + /* + * features shortened by the cut (delete on Undo) + */ + Map> truncatedFeatures; AlignmentI al; @@ -1379,11 +1304,8 @@ public class EditCommand implements CommandI Edit(Action cmd, SequenceI[] sqs, int pos, int count, AlignmentI align) { - this.gapChar = align.getGapCharacter(); - this.command = cmd; - this.seqs = sqs; - this.position = pos; - this.number = count; + this(cmd, sqs, pos, count, align.getGapCharacter()); + this.al = align; alIndex = new int[sqs.length]; @@ -1398,19 +1320,13 @@ public class EditCommand implements CommandI Edit(Action cmd, SequenceI[] sqs, int pos, int count, AlignmentI align, String replace) { - this.command = cmd; - this.seqs = sqs; - this.position = pos; - this.number = count; - this.al = align; - this.gapChar = align.getGapCharacter(); + this(cmd, sqs, pos, count, align); + string = new char[sqs.length][]; for (int i = 0; i < sqs.length; i++) { string[i] = replace.toCharArray(); } - - fullAlignmentHeight = (align.getHeight() == sqs.length); } public SequenceI[] getSequences() @@ -1457,4 +1373,143 @@ public class EditCommand implements CommandI return new ReverseListIterator(getEdits()); } } + + /** + * Adjusts features for Cut, and saves details of changes made to allow Undo + *
    + *
  • features left of the cut are unchanged
  • + *
  • features right of the cut are shifted left
  • + *
  • features internal to the cut region are deleted
  • + *
  • features that overlap or span the cut are shortened
  • + *
  • the originals of any deleted or shorted features are saved, to re-add + * on Undo
  • + *
  • any added (shortened) features are saved, to delete on Undo
  • + *
+ * + * @param command + * @param seq + * @param fromPosition + * @param toPosition + * @param cutIsInternal + */ + protected static void cutFeatures(Edit command, SequenceI seq, + int fromPosition, int toPosition, boolean cutIsInternal) + { + 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); + + /* + * adjust start-end of overlapping features; + * delete features enclosed by the cut; + * delete partially overlapping contact features + */ + for (SequenceFeature sf : toAmend) + { + int sfBegin = sf.getBegin(); + int sfEnd = sf.getEnd(); + int newBegin = sfBegin; + int newEnd = sfEnd; + boolean toDelete = false; + boolean follows = false; + + if (sfBegin >= cutStartPos && sfEnd <= cutEndPos) + { + /* + * feature lies within cut region - delete it + */ + toDelete = true; + } + else if (sfBegin < cutStartPos && sfEnd > cutEndPos) + { + /* + * feature spans cut region - left-shift the end + */ + newEnd -= cutWidth; + } + else if (sfEnd <= cutEndPos) + { + /* + * feature overlaps left of cut region - truncate right + */ + newEnd = cutStartPos - 1; + if (sf.isContactFeature()) + { + toDelete = true; + } + } + else if (sfBegin >= cutStartPos) + { + /* + * remaining case - feature overlaps right + * truncate left, adjust end of feature + */ + newBegin = cutIsInternal ? cutStartPos : cutEndPos + 1; + // newEnd = newBegin + (sfEnd - sfBegin) - overlapsBy; + newEnd = newBegin + sfEnd - cutEndPos - 1; + if (sf.isContactFeature()) + { + toDelete = true; + } + } + + seq.deleteFeature(sf); + if (!follows) + { + removed.add(sf); + } + if (!toDelete) + { + SequenceFeature copy = new SequenceFeature(sf, newBegin, newEnd, + sf.getFeatureGroup(), sf.getScore()); + seq.addSequenceFeature(copy); + if (!follows) + { + added.add(copy); + } + } + } + + /* + * and left shift any features lying to the right of the cut region + * (but not if the cut is at start or end of sequence) + */ + if (cutIsInternal) + { + featureStore.shiftFeatures(cutEndPos + 1, -cutWidth); + } + } + + /* + * save deleted and amended features, so that Undo can + * re-add or delete them respectively + */ + if (command.deletedFeatures == null) + { + command.deletedFeatures = new HashMap<>(); + } + if (command.truncatedFeatures == null) + { + command.truncatedFeatures = new HashMap<>(); + } + command.deletedFeatures.put(seq, removed); + command.truncatedFeatures.put(seq, added); + } } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index b758d51..80de543 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -724,6 +724,7 @@ public class Sequence extends ASequence implements SequenceI * preserve end residue column provided cursor was valid */ int endColumn = isValidCursor(cursor) ? cursor.lastColumnPosition : 0; + if (residuePos == this.end) { endColumn = column; @@ -760,8 +761,7 @@ public class Sequence extends ASequence implements SequenceI /* * move left or right to find pos from hint.position */ - int col = curs.columnPosition - 1; // convert from base 1 to 0-based array - // index + int col = curs.columnPosition - 1; // convert from base 1 to base 0 int newPos = curs.residuePosition; int delta = newPos > pos ? -1 : 1; @@ -1818,6 +1818,15 @@ public class Sequence extends ASequence implements SequenceI List result = getFeatures().findFeatures(startPos, endPos, types); + if (datasetSequence != null) + { + result = datasetSequence.getFeatures().findFeatures(startPos, endPos, + types); + } + else + { + result = sequenceFeatureStore.findFeatures(startPos, endPos, types); + } /* * if end column is gapped, endPos may be to the right, @@ -1898,75 +1907,4 @@ public class Sequence extends ASequence implements SequenceI return count; } - - @Override - public List adjustFeatures(int fromColumn, int toColumn) - { - List amended = new ArrayList<>(); - - if (toColumn < fromColumn) - { - return amended; - } - - synchronized (sequenceFeatureStore) - { - /* - * get features that overlap or span the cut region - */ - List overlaps = findFeatures(fromColumn, toColumn); - int cutWidth = toColumn - fromColumn + 1; - - /* - * get features that strictly follow the cut region, - * and shift them left by the width of the cut - */ - List follow = findFeatures(toColumn + 1, - Integer.MAX_VALUE); - follow.removeAll(overlaps); - for (SequenceFeature sf : follow) - { - SequenceFeature copy = new SequenceFeature(sf, sf.getBegin() - - cutWidth, sf.getEnd() - cutWidth, sf.getFeatureGroup(), - sf.getScore()); - deleteFeature(sf); - addSequenceFeature(copy); - } - - /* - * adjust start-end of overlapping features, and delete if enclosed by - * the cut, or a partially overlapping contact feature - */ - for (SequenceFeature sf : overlaps) - { - // TODO recode to compute newBegin, newEnd, isDelete - // then perform the action - int sfBegin = sf.getBegin(); - int sfEnd = sf.getEnd(); - int startCol = findIndex(sfBegin); - int endCol = findIndex(sfEnd); - if (startCol >= fromColumn && endCol <= toColumn) - { - // within cut region - delete feature - deleteFeature(sf); - amended.add(new SequenceFeature[] { sf, null }); - continue; - } - if (startCol < fromColumn && endCol > toColumn) - { - // feature spans cut region - shift end left - SequenceFeature copy = new SequenceFeature(sf, sf.getBegin(), - sf.getEnd() - cutWidth, sf.getFeatureGroup(), - sf.getScore()); - deleteFeature(sf); - addSequenceFeature(copy); - amended.add(new SequenceFeature[] { sf, copy }); - continue; - } - // todo partial overlap - delete if contact feature - } - } - - return amended; - } } diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index 538b791..d76c5a7 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -534,22 +534,4 @@ public interface SequenceI extends ASequenceI * @param c2 */ int replace(char c1, char c2); - - /** - * Adjusts position and extent of features to allow for cut of the specified - * (inclusive) column range. Returns a list of {originalFeature, - * amendedFeature} for - *
    - *
  • features that have been deleted (as within the cut) - amendedFeature is - * null
  • - *
  • truncated features (as overlapping or spanning the cut)
  • - *
- * Contact features that overlap the cut region are deleted. Contact features - * that enclose the cut region are shortened. - * - * @param fromColumn - * @param toColumn - * @return - */ - List adjustFeatures(int fromColumn, int toColumn); } diff --git a/test/jalview/commands/EditCommandTest.java b/test/jalview/commands/EditCommandTest.java index 7f1a432..9177135 100644 --- a/test/jalview/commands/EditCommandTest.java +++ b/test/jalview/commands/EditCommandTest.java @@ -21,8 +21,8 @@ package jalview.commands; import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; +import static org.testng.AssertJUnit.assertTrue; import jalview.commands.EditCommand.Action; import jalview.commands.EditCommand.Edit; @@ -663,7 +663,7 @@ public class EditCommandTest * create sequence features before, after and overlapping * a cut of columns/residues 4-7 */ - SequenceI seq0 = seqs[0]; + SequenceI seq0 = seqs[0]; // abcdefghjk/1-10 seq0.addSequenceFeature(new SequenceFeature("before", "", 1, 3, 0f, null)); seq0.addSequenceFeature(new SequenceFeature("overlap left", "", 2, 6, @@ -691,13 +691,13 @@ public class EditCommandTest assertEquals(2, sf.getBegin()); assertEquals(3, sf.getEnd()); // truncated by cut sf = sfs.get(2); - assertEquals("overlap right", sf.getType()); - assertEquals(4, sf.getBegin()); // shifted left by cut - assertEquals(5, sf.getEnd()); // truncated by cut - sf = sfs.get(3); assertEquals("after", sf.getType()); assertEquals(4, sf.getBegin()); // shifted left by cut assertEquals(6, sf.getEnd()); // shifted left by cut + sf = sfs.get(3); + assertEquals("overlap right", sf.getType()); + assertEquals(4, sf.getBegin()); // shifted left by cut + assertEquals(4, sf.getEnd()); // truncated by cut } /** @@ -719,8 +719,7 @@ public class EditCommandTest { String desc = String.format("%d-%d", from, to); SequenceFeature sf = new SequenceFeature("test", desc, from, to, - 0f, - null); + 0f, null); sf.setValue("from", Integer.valueOf(from)); sf.setValue("to", Integer.valueOf(to)); seq0.addSequenceFeature(sf); @@ -753,8 +752,8 @@ public class EditCommandTest String msg = String.format("Cut %d-%d ", from, to); if (to - from == 4) { - // all columns cut - assertNull(sfs); + // all columns were cut + assertTrue(sfs.isEmpty()); } else { @@ -765,18 +764,17 @@ public class EditCommandTest /* * inspect individual features */ - if (sfs != null) + for (SequenceFeature sf : sfs) { - for (SequenceFeature sf : sfs) - { - checkFeatureRelocation(sf, from + 1, to + 1); - } + checkFeatureRelocation(sf, from + 1, to + 1, from > 0); } + /* * undo ready for next cut */ testee.undoCommand(new AlignmentI[] { alignment }); - assertEquals(func(5), seq0.getSequenceFeatures().size()); + int size = seq0.getSequenceFeatures().size(); + assertEquals(func(5), size); } } } @@ -789,13 +787,15 @@ public class EditCommandTest * start of cut (first residue cut) * @param to * end of cut (last residue cut) + * @param newDataset */ - private void checkFeatureRelocation(SequenceFeature sf, int from, int to) + private void checkFeatureRelocation(SequenceFeature sf, int from, int to, + boolean newDataset) { // TODO handle the gapped sequence case as well int cutSize = to - from + 1; - int oldFrom = ((Integer) sf.getValue("from")).intValue(); - int oldTo = ((Integer) sf.getValue("to")).intValue(); + final int oldFrom = ((Integer) sf.getValue("from")).intValue(); + final int oldTo = ((Integer) sf.getValue("to")).intValue(); String msg = String.format( "Feature %s relocated to %d-%d after cut of %d-%d", @@ -809,8 +809,10 @@ public class EditCommandTest else if (oldFrom > to) { // follows cut region - shift by size of cut - assertEquals("3: " + msg, oldFrom - cutSize, sf.getBegin()); - assertEquals("4: " + msg, oldTo - cutSize, sf.getEnd()); + assertEquals("3: " + msg, newDataset ? oldFrom - cutSize : oldFrom, + sf.getBegin()); + assertEquals("4: " + msg, newDataset ? oldTo - cutSize : oldTo, + sf.getEnd()); } else if (oldFrom < from && oldTo > to) { @@ -826,8 +828,9 @@ public class EditCommandTest else if (oldTo > to) { // feature overlaps right side of cut region - truncated left - assertEquals("8: " + msg, from, sf.getBegin()); - assertEquals("9: " + msg, from + oldTo - to - 1, sf.getEnd()); + assertEquals("8: " + msg, newDataset ? from : to + 1, sf.getBegin()); + assertEquals("9: " + msg, newDataset ? from + oldTo - to - 1 : oldTo, + sf.getEnd()); } else { @@ -840,29 +843,33 @@ public class EditCommandTest * Test a cut action's relocation of sequence features */ @Test(groups = { "Functional" }) - public void testCut_gappedWithFeatures() + public void testCut_withFeatures5prime() { + SequenceI seq0 = new Sequence("seq/8-11", "A-BCC"); + seq0.createDatasetSequence(); + assertEquals(8, seq0.getStart()); + seq0.addSequenceFeature(new SequenceFeature("", "", 10, 11, 0f, + null)); + SequenceI[] seqsArray = new SequenceI[] { seq0 }; + AlignmentI alignment = new Alignment(seqsArray); + /* - * create sequence features before, after and overlapping - * a cut of columns/residues 4-7 + * cut columns of A-B; same dataset sequence is retained, aligned sequence + * start becomes 10 */ - SequenceI seq0 = new Sequence("seq", "A-BCC"); - seq0.addSequenceFeature(new SequenceFeature("", "", 3, 4, 0f, - null)); - AlignmentI alignment = new Alignment(new SequenceI[] { seq0 }); - // cut columns of A-B - Edit ec = testee.new Edit(Action.CUT, seqs, 0, 3, alignment); // cols 0-3 - // base 0 + Edit ec = testee.new Edit(Action.CUT, seqsArray, 0, 3, alignment); EditCommand.cut(ec, new AlignmentI[] { alignment }); /* - * feature on CC(3-4) should now be on CC(1-2) + * feature on CC(10-11) should still be on CC(10-11) */ + assertSame(seq0, alignment.getSequenceAt(0)); + assertEquals(10, seq0.getStart()); List sfs = seq0.getSequenceFeatures(); assertEquals(1, sfs.size()); SequenceFeature sf = sfs.get(0); - assertEquals(1, sf.getBegin()); - assertEquals(2, sf.getEnd()); + assertEquals(10, sf.getBegin()); + assertEquals(11, sf.getEnd()); // TODO add further cases including Undo - see JAL-2541 } diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java index 6844072..cc3c090 100644 --- a/test/jalview/datamodel/SequenceTest.java +++ b/test/jalview/datamodel/SequenceTest.java @@ -263,6 +263,61 @@ public class SequenceTest assertEquals(13, sq.findIndex(99)); } + @Test(groups = { "Functional" }) + public void testFindPositions() + { + SequenceI sq = new Sequence("test/8-13", "-ABC---DE-F--"); + + /* + * invalid inputs + */ + assertNull(sq.findPositions(6, 5)); + assertNull(sq.findPositions(0, 5)); + assertNull(sq.findPositions(-1, 5)); + + /* + * all gapped ranges + */ + assertNull(sq.findPositions(1, 1)); // 1-based columns + assertNull(sq.findPositions(5, 5)); + assertNull(sq.findPositions(5, 6)); + assertNull(sq.findPositions(5, 7)); + + /* + * all ungapped ranges + */ + assertEquals(new Range(8, 8), sq.findPositions(2, 2)); // A + assertEquals(new Range(8, 9), sq.findPositions(2, 3)); // AB + assertEquals(new Range(8, 10), sq.findPositions(2, 4)); // ABC + assertEquals(new Range(9, 10), sq.findPositions(3, 4)); // BC + + /* + * gap to ungapped range + */ + assertEquals(new Range(8, 10), sq.findPositions(1, 4)); // ABC + assertEquals(new Range(11, 12), sq.findPositions(6, 9)); // DE + + /* + * ungapped to gapped range + */ + assertEquals(new Range(10, 10), sq.findPositions(4, 5)); // C + assertEquals(new Range(9, 13), sq.findPositions(3, 11)); // BCDEF + + /* + * ungapped to ungapped enclosing gaps + */ + assertEquals(new Range(10, 11), sq.findPositions(4, 8)); // CD + assertEquals(new Range(8, 13), sq.findPositions(2, 11)); // ABCDEF + + /* + * gapped to gapped enclosing ungapped + */ + assertEquals(new Range(8, 10), sq.findPositions(1, 5)); // ABC + assertEquals(new Range(11, 12), sq.findPositions(5, 10)); // DE + assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot + assertEquals(new Range(8, 13), sq.findPositions(1, 99)); + } + /** * Tests for the method that returns a dataset sequence position (start..) for * an aligned column position (base 0). @@ -438,8 +493,7 @@ public class SequenceTest assertEquals("test:Pos13:Col10:startCol3:endCol10:tok0", PA.getValue(sq, "cursor").toString()); sq.sequenceChanged(); - assertEquals(12, sq.findPosition(8)); - cursor = (SequenceCursor) PA.getValue(sq, "cursor"); + assertEquals(12, sq.findPosition(8)); // E12 // sequenceChanged() invalidates cursor.lastResidueColumn cursor = (SequenceCursor) PA.getValue(sq, "cursor"); assertEquals("test:Pos12:Col9:startCol3:endCol0:tok1", @@ -478,6 +532,13 @@ public class SequenceTest assertEquals(6, sq.getEnd()); assertNull(PA.getValue(sq, "datasetSequence")); + sq = new Sequence("test", "ABCDE"); + sq.deleteChars(0, 3); + assertEquals("DE", sq.getSequenceAsString()); + assertEquals(4, sq.getStart()); + assertEquals(5, sq.getEnd()); + assertNull(PA.getValue(sq, "datasetSequence")); + /* * delete at end */ @@ -1540,6 +1601,10 @@ public class SequenceTest // cursor should now be at [D 6] cursor = (SequenceCursor) PA.getValue(sq, "cursor"); assertEquals(new SequenceCursor(sq, 11, 6, ++token), cursor); + assertEquals(0, cursor.lastColumnPosition); // not yet found + assertEquals(13, sq.findPosition(8)); // E13 + cursor = (SequenceCursor) PA.getValue(sq, "cursor"); + assertEquals(9, cursor.lastColumnPosition); // found /* * deleteChars should invalidate the cached cursor @@ -1617,59 +1682,4 @@ public class SequenceTest assertEquals(".K..BCD.EF..", sq.getSequenceAsString()); assertEquals(2, PA.getValue(sq, "changeCount")); } - - @Test(groups = { "Functional" }) - public void testFindPositions() - { - SequenceI sq = new Sequence("test/8-13", "-ABC---DE-F--"); - - /* - * invalid inputs - */ - assertNull(sq.findPositions(6, 5)); - assertNull(sq.findPositions(0, 5)); - assertNull(sq.findPositions(-1, 5)); - - /* - * all gapped ranges - */ - assertNull(sq.findPositions(1, 1)); // 1-based columns - assertNull(sq.findPositions(5, 5)); - assertNull(sq.findPositions(5, 6)); - assertNull(sq.findPositions(5, 7)); - - /* - * all ungapped ranges - */ - assertEquals(new Range(8, 8), sq.findPositions(2, 2)); // A - assertEquals(new Range(8, 9), sq.findPositions(2, 3)); // AB - assertEquals(new Range(8, 10), sq.findPositions(2, 4)); // ABC - assertEquals(new Range(9, 10), sq.findPositions(3, 4)); // BC - - /* - * gap to ungapped range - */ - assertEquals(new Range(8, 10), sq.findPositions(1, 4)); // ABC - assertEquals(new Range(11, 12), sq.findPositions(6, 9)); // DE - - /* - * ungapped to gapped range - */ - assertEquals(new Range(10, 10), sq.findPositions(4, 5)); // C - assertEquals(new Range(9, 13), sq.findPositions(3, 11)); // BCDEF - - /* - * ungapped to ungapped enclosing gaps - */ - assertEquals(new Range(10, 11), sq.findPositions(4, 8)); // CD - assertEquals(new Range(8, 13), sq.findPositions(2, 11)); // ABCDEF - - /* - * gapped to gapped enclosing ungapped - */ - assertEquals(new Range(8, 10), sq.findPositions(1, 5)); // ABC - assertEquals(new Range(11, 12), sq.findPositions(5, 10)); // DE - assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot - assertEquals(new Range(8, 13), sq.findPositions(1, 99)); - } } -- 1.7.10.2