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;
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);
}
/**
// 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.
command.oldds = new SequenceI[command.seqs.length];
}
command.oldds[i] = oldds;
- // FIXME JAL-2541 JAL-2526 get correct positions if on a gap
- List<SequenceFeature[]> 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);
}
}
}
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)
}
/*
- * 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<SequenceFeature> sf = sequence.getFeatures()
- // .getPositionalFeatures();
- //
- // if (sf.isEmpty())
- // {
- // return;
- // }
- //
- // List<SequenceFeature> oldsf = new ArrayList<SequenceFeature>();
- //
- // 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<SequenceI,
- // List<SequenceFeature>>();
- // }
- //
- // 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);
+ }
+ }
}
/**
Map<String, Annotation[]> deletedAnnotations;
- Map<SequenceI, List<SequenceFeature[]>> editedFeatures;
+ /*
+ * features deleted by the cut (re-add on Undo)
+ */
+ Map<SequenceI, List<SequenceFeature>> deletedFeatures;
+
+ /*
+ * features shortened by the cut (delete on Undo)
+ */
+ Map<SequenceI, List<SequenceFeature>> truncatedFeatures;
AlignmentI al;
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];
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()
return new ReverseListIterator<Edit>(getEdits());
}
}
+
+ /**
+ * Adjusts features for Cut, and saves details of changes made to allow Undo
+ * <ul>
+ * <li>features left of the cut are unchanged</li>
+ * <li>features right of the cut are shifted left</li>
+ * <li>features internal to the cut region are deleted</li>
+ * <li>features that overlap or span the cut are shortened</li>
+ * <li>the originals of any deleted or shorted features are saved, to re-add
+ * on Undo</li>
+ * <li>any added (shortened) features are saved, to delete on Undo</li>
+ * </ul>
+ *
+ * @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<SequenceFeature> added = new ArrayList<>();
+ List<SequenceFeature> 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<SequenceFeature> 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);
+ }
}
* preserve end residue column provided cursor was valid
*/
int endColumn = isValidCursor(cursor) ? cursor.lastColumnPosition : 0;
+
if (residuePos == this.end)
{
endColumn = column;
/*
* 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;
List<SequenceFeature> 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,
return count;
}
-
- @Override
- public List<SequenceFeature[]> adjustFeatures(int fromColumn, int toColumn)
- {
- List<SequenceFeature[]> amended = new ArrayList<>();
-
- if (toColumn < fromColumn)
- {
- return amended;
- }
-
- synchronized (sequenceFeatureStore)
- {
- /*
- * get features that overlap or span the cut region
- */
- List<SequenceFeature> 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<SequenceFeature> 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;
- }
}
* @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
- * <ul>
- * <li>features that have been deleted (as within the cut) - amendedFeature is
- * null</li>
- * <li>truncated features (as overlapping or spanning the cut)</li>
- * </ul>
- * Contact features that overlap the cut region are deleted. Contact features
- * that enclose the cut region are shortened.
- *
- * @param fromColumn
- * @param toColumn
- * @return
- */
- List<SequenceFeature[]> adjustFeatures(int fromColumn, int toColumn);
}
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;
* 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,
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
}
/**
{
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);
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
{
/*
* 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);
}
}
}
* 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",
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)
{
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
{
* 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<SequenceFeature> 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
}
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).
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",
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
*/
// 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
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));
- }
}