JAL-3794 new test for recognition of protein or DNA with ambiguity characters, includ...
[jalview.git] / test / jalview / datamodel / SequenceTest.java
index 216635d..6d07113 100644 (file)
@@ -34,6 +34,7 @@ import jalview.commands.EditCommand.Action;
 import jalview.datamodel.PDBEntry.Type;
 import jalview.gui.JvOptionPane;
 import jalview.util.MapList;
+import jalview.ws.params.InvalidArgumentException;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -109,6 +110,23 @@ public class SequenceTest
     assertTrue(sq.isProtein());
   }
 
+  @Test(groups = ("Functional"))
+  public void testIsProteinWithXorNAmbiguityCodes()
+  {
+    // test Protein with N - poly asparagine 
+    assertTrue(new Sequence("prot", "ASDFASDFASDFNNNNNNNNN").isProtein());
+    assertTrue(new Sequence("prot", "NNNNNNNNNNNNNNNNNNNNN").isProtein());
+    // test Protein with X
+    assertTrue(new Sequence("prot", "ASDFASDFASDFXXXXXXXXX").isProtein());
+    // test DNA with X
+    assertFalse(new Sequence("prot", "ACGTACGTACGTXXXXXXXX").isProtein());
+    // test DNA with N
+    assertFalse(new Sequence("prot", "ACGTACGTACGTNNNNNNNN").isProtein());
+    // test RNA with X
+    assertFalse(new Sequence("prot", "ACGUACGUACGUXXXXXXXXX").isProtein());
+    assertFalse(new Sequence("prot", "ACGUACGUACGUNNNNNNNNN").isProtein());
+  }
+
   @Test(groups = { "Functional" })
   public void testGetAnnotation()
   {
@@ -289,6 +307,61 @@ public class SequenceTest
     assertEquals(0, sq.findIndex(2));
   }
 
+  @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).
@@ -303,7 +376,7 @@ public class SequenceTest
     SequenceI sq = new Sequence("test/8-13", "ABCDEF");
     assertEquals(8, sq.findPosition(0));
     // Sequence should now hold a cursor at [8, 0]
-    assertEquals("test:Pos8:Col1:startCol1:endCol0:tok0",
+    assertEquals("test:Pos8:Col1:startCol1:endCol0:tok1",
             PA.getValue(sq, "cursor").toString());
     SequenceCursor cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     int token = (int) PA.getValue(sq, "changeCount");
@@ -319,24 +392,24 @@ public class SequenceTest
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(++token, (int) PA.getValue(sq, "changeCount"));
     assertEquals(new SequenceCursor(sq, 13, 6, token), cursor);
-    assertEquals("test:Pos13:Col6:startCol1:endCol6:tok1",
+    assertEquals("test:Pos13:Col6:startCol1:endCol6:tok2",
             PA.getValue(sq, "cursor").toString());
 
     // assertEquals(-1, seq.findPosition(6)); // fails
 
     sq = new Sequence("test/8-11", "AB-C-D--");
-    token = (int) PA.getValue(sq, "changeCount"); // 0
+    token = (int) PA.getValue(sq, "changeCount"); // 1 for setStart
     assertEquals(8, sq.findPosition(0));
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 8, 1, token), cursor);
-    assertEquals("test:Pos8:Col1:startCol1:endCol0:tok0",
+    assertEquals("test:Pos8:Col1:startCol1:endCol0:tok1",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(9, sq.findPosition(1));
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 9, 2, ++token), cursor);
-    assertEquals("test:Pos9:Col2:startCol1:endCol0:tok1",
+    assertEquals("test:Pos9:Col2:startCol1:endCol0:tok2",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
@@ -345,14 +418,14 @@ public class SequenceTest
     assertEquals(10, sq.findPosition(2));
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 9, 2, ++token), cursor);
-    assertEquals("test:Pos9:Col2:startCol1:endCol0:tok2",
+    assertEquals("test:Pos9:Col2:startCol1:endCol0:tok3",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(10, sq.findPosition(3));
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 10, 4, ++token), cursor);
-    assertEquals("test:Pos10:Col4:startCol1:endCol0:tok3",
+    assertEquals("test:Pos10:Col4:startCol1:endCol0:tok4",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
@@ -361,7 +434,7 @@ public class SequenceTest
     assertEquals(11, sq.findPosition(4));
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 10, 4, ++token), cursor);
-    assertEquals("test:Pos10:Col4:startCol1:endCol0:tok4",
+    assertEquals("test:Pos10:Col4:startCol1:endCol0:tok5",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
@@ -369,7 +442,7 @@ public class SequenceTest
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 11, 6, ++token), cursor);
     // lastCol has been found and saved in the cursor
-    assertEquals("test:Pos11:Col6:startCol1:endCol6:tok5",
+    assertEquals("test:Pos11:Col6:startCol1:endCol6:tok6",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
@@ -385,6 +458,7 @@ public class SequenceTest
     sq = new Sequence("test/8-13", "--AB-C-DEF--");
     assertEquals(8, sq.findPosition(0));
     assertNull(PA.getValue(sq, "cursor"));
+    assertEquals(1, PA.getValue(sq, "changeCount"));
 
     sq.sequenceChanged();
     assertEquals(8, sq.findPosition(1));
@@ -392,41 +466,41 @@ public class SequenceTest
 
     sq.sequenceChanged();
     assertEquals(8, sq.findPosition(2));
-    assertEquals("test:Pos8:Col3:startCol3:endCol0:tok2",
+    assertEquals("test:Pos8:Col3:startCol3:endCol0:tok3",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(9, sq.findPosition(3));
-    assertEquals("test:Pos9:Col4:startCol3:endCol0:tok3",
+    assertEquals("test:Pos9:Col4:startCol3:endCol0:tok4",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     // column[4] is a gap, returns next residue pos (C10)
     // cursor is set to last residue found [B]
     assertEquals(10, sq.findPosition(4));
-    assertEquals("test:Pos9:Col4:startCol3:endCol0:tok4",
+    assertEquals("test:Pos9:Col4:startCol3:endCol0:tok5",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(10, sq.findPosition(5));
-    assertEquals("test:Pos10:Col6:startCol3:endCol0:tok5",
+    assertEquals("test:Pos10:Col6:startCol3:endCol0:tok6",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     // column[6] is a gap, returns next residue pos (D11)
     // cursor is set to last residue found [C]
     assertEquals(11, sq.findPosition(6));
-    assertEquals("test:Pos10:Col6:startCol3:endCol0:tok6",
+    assertEquals("test:Pos10:Col6:startCol3:endCol0:tok7",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(11, sq.findPosition(7));
-    assertEquals("test:Pos11:Col8:startCol3:endCol0:tok7",
+    assertEquals("test:Pos11:Col8:startCol3:endCol0:tok8",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(12, sq.findPosition(8));
-    assertEquals("test:Pos12:Col9:startCol3:endCol0:tok8",
+    assertEquals("test:Pos12:Col9:startCol3:endCol0:tok9",
             PA.getValue(sq, "cursor").toString());
 
     /*
@@ -434,12 +508,12 @@ public class SequenceTest
      */
     sq.sequenceChanged();
     assertEquals(13, sq.findPosition(9));
-    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok9",
+    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok10",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(14, sq.findPosition(10));
-    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok10",
+    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok11",
             PA.getValue(sq, "cursor").toString());
 
     /*
@@ -448,12 +522,12 @@ public class SequenceTest
      */
     sq.sequenceChanged();
     assertEquals(14, sq.findPosition(11));
-    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok11",
+    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok12",
             PA.getValue(sq, "cursor").toString());
 
     sq.sequenceChanged();
     assertEquals(14, sq.findPosition(99));
-    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok12",
+    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok13",
             PA.getValue(sq, "cursor").toString());
 
     /*
@@ -461,20 +535,19 @@ public class SequenceTest
      */
     sq = new Sequence("test/8-13", "--AB-C-DEF");
     assertEquals(13, sq.findPosition(9));
-    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok0",
+    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok1",
             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("test:Pos12:Col9:startCol3:endCol0:tok2",
             cursor.toString());
     // findPosition with cursor accepts base 1 column values
     assertEquals(13, ((Sequence) sq).findPosition(10, cursor));
     assertEquals(13, sq.findPosition(9)); // F13
     // lastResidueColumn has now been found and saved in cursor
-    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok1",
+    assertEquals("test:Pos13:Col10:startCol3:endCol10:tok2",
             PA.getValue(sq, "cursor").toString());
   }
 
@@ -504,6 +577,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
      */
@@ -513,6 +593,21 @@ public class SequenceTest
     assertEquals(1, sq.getStart());
     assertEquals(4, sq.getEnd());
     assertNull(PA.getValue(sq, "datasetSequence"));
+
+    /*
+     * delete more positions than there are
+     */
+    sq = new Sequence("test/8-11", "ABCD");
+    sq.deleteChars(0, 99);
+    assertEquals("", sq.getSequenceAsString());
+    assertEquals(12, sq.getStart()); // = findPosition(99) ?!?
+    assertEquals(11, sq.getEnd());
+
+    sq = new Sequence("test/8-11", "----");
+    sq.deleteChars(0, 99); // ArrayIndexOutOfBoundsException <= 2.10.2
+    assertEquals("", sq.getSequenceAsString());
+    assertEquals(8, sq.getStart());
+    assertEquals(11, sq.getEnd());
   }
 
   @Test(groups = { "Functional" })
@@ -538,9 +633,9 @@ public class SequenceTest
     assertNotNull(newDs);
     assertNotSame(ds, newDs);
     assertNotNull(sq.getDBRefs());
-    assertEquals(1, sq.getDBRefs().length);
-    assertNotSame(dbr1, sq.getDBRefs()[0]);
-    assertEquals(dbr1, sq.getDBRefs()[0]);
+    assertEquals(1, sq.getDBRefs().size());
+    assertNotSame(dbr1, sq.getDBRefs().get(0));
+    assertEquals(dbr1, sq.getDBRefs().get(0));
 
     /*
      * internal delete with sequence features
@@ -601,8 +696,8 @@ public class SequenceTest
     assertEquals(4, sq.getEnd());
     assertSame(ds, PA.getValue(sq, "datasetSequence"));
     assertNotNull(sq.getDBRefs());
-    assertEquals(1, sq.getDBRefs().length);
-    assertSame(dbr1, sq.getDBRefs()[0]);
+    assertEquals(1, sq.getDBRefs().size());
+    assertSame(dbr1, sq.getDBRefs().get(0));
   }
 
   @Test(groups = { "Functional" })
@@ -836,12 +931,12 @@ public class SequenceTest
             new AlignmentAnnotation("Test annot", "Test annot description",
                     annots));
     Assert.assertEquals(sq.getDescription(), "Test sequence description..");
-    Assert.assertEquals(sq.getDBRefs().length, 5); // DBRefs are on dataset
+    Assert.assertEquals(sq.getDBRefs().size(), 5); // DBRefs are on dataset
                                                    // sequence
     Assert.assertEquals(sq.getAllPDBEntries().size(), 4);
     Assert.assertNotNull(sq.getAnnotation());
     Assert.assertEquals(sq.getAnnotation()[0].annotations.length, 2);
-    Assert.assertEquals(sq.getDatasetSequence().getDBRefs().length, 5); // same
+    Assert.assertEquals(sq.getDatasetSequence().getDBRefs().size(), 5); // same
                                                                         // as
                                                                         // sq.getDBRefs()
     Assert.assertEquals(sq.getDatasetSequence().getAllPDBEntries().size(),
@@ -852,11 +947,11 @@ public class SequenceTest
 
     Assert.assertEquals(derived.getDescription(),
             "Test sequence description..");
-    Assert.assertEquals(derived.getDBRefs().length, 5); // come from dataset
+    Assert.assertEquals(derived.getDBRefs().size(), 5); // come from dataset
     Assert.assertEquals(derived.getAllPDBEntries().size(), 4);
     Assert.assertNotNull(derived.getAnnotation());
     Assert.assertEquals(derived.getAnnotation()[0].annotations.length, 2);
-    Assert.assertEquals(derived.getDatasetSequence().getDBRefs().length, 5);
+    Assert.assertEquals(derived.getDatasetSequence().getDBRefs().size(), 5);
     Assert.assertEquals(derived.getDatasetSequence().getAllPDBEntries()
             .size(), 4);
     Assert.assertNotNull(derived.getDatasetSequence().getAnnotation());
@@ -935,10 +1030,10 @@ public class SequenceTest
     // but that doesn't distinguish it from an aligned sequence
     // which has not yet generated a dataset sequence
     // NB getDBRef looks inside dataset sequence if not null
-    DBRefEntry[] dbrefs = copy.getDBRefs();
-    assertEquals(1, dbrefs.length);
-    assertFalse(dbrefs[0] == seq1.getDBRefs()[0]);
-    assertTrue(dbrefs[0].equals(seq1.getDBRefs()[0]));
+    List<DBRefEntry> dbrefs = copy.getDBRefs();
+    assertEquals(1, dbrefs.size());
+    assertFalse(dbrefs.get(0) == seq1.getDBRefs().get(0));
+    assertTrue(dbrefs.get(0).equals(seq1.getDBRefs().get(0)));
   }
 
   @Test(groups = { "Functional" })
@@ -967,9 +1062,9 @@ public class SequenceTest
 
     // getDBRef looks inside dataset sequence and this is shared,
     // so holds the same dbref objects
-    DBRefEntry[] dbrefs = copy.getDBRefs();
-    assertEquals(1, dbrefs.length);
-    assertSame(dbrefs[0], seq1.getDBRefs()[0]);
+    List<DBRefEntry> dbrefs = copy.getDBRefs();
+    assertEquals(1, dbrefs.size());
+    assertSame(dbrefs.get(0), seq1.getDBRefs().get(0));
   }
 
   /**
@@ -1068,39 +1163,39 @@ public class SequenceTest
     assertNull(sq.getDBRefs());
     DBRefEntry dbref = new DBRefEntry("Uniprot", "1", "P00340");
     sq.addDBRef(dbref);
-    assertEquals(1, sq.getDBRefs().length);
-    assertSame(dbref, sq.getDBRefs()[0]);
+    assertEquals(1, sq.getDBRefs().size());
+    assertSame(dbref, sq.getDBRefs().get(0));
 
     /*
      * change of version - new entry
      */
     DBRefEntry dbref2 = new DBRefEntry("Uniprot", "2", "P00340");
     sq.addDBRef(dbref2);
-    assertEquals(2, sq.getDBRefs().length);
-    assertSame(dbref, sq.getDBRefs()[0]);
-    assertSame(dbref2, sq.getDBRefs()[1]);
+    assertEquals(2, sq.getDBRefs().size());
+    assertSame(dbref, sq.getDBRefs().get(0));
+    assertSame(dbref2, sq.getDBRefs().get(1));
 
     /*
      * matches existing entry - not added
      */
     sq.addDBRef(new DBRefEntry("UNIPROT", "1", "p00340"));
-    assertEquals(2, sq.getDBRefs().length);
+    assertEquals(2, sq.getDBRefs().size());
 
     /*
      * different source = new entry
      */
     DBRefEntry dbref3 = new DBRefEntry("UniRef", "1", "p00340");
     sq.addDBRef(dbref3);
-    assertEquals(3, sq.getDBRefs().length);
-    assertSame(dbref3, sq.getDBRefs()[2]);
+    assertEquals(3, sq.getDBRefs().size());
+    assertSame(dbref3, sq.getDBRefs().get(2));
 
     /*
      * different ref = new entry
      */
     DBRefEntry dbref4 = new DBRefEntry("UniRef", "1", "p00341");
     sq.addDBRef(dbref4);
-    assertEquals(4, sq.getDBRefs().length);
-    assertSame(dbref4, sq.getDBRefs()[3]);
+    assertEquals(4, sq.getDBRefs().size());
+    assertSame(dbref4, sq.getDBRefs().get(3));
 
     /*
      * matching ref with a mapping - map updated
@@ -1110,8 +1205,8 @@ public class SequenceTest
         1, 1 }, 3, 1));
     dbref5.setMap(map);
     sq.addDBRef(dbref5);
-    assertEquals(4, sq.getDBRefs().length);
-    assertSame(dbref4, sq.getDBRefs()[3]);
+    assertEquals(4, sq.getDBRefs().size());
+    assertSame(dbref4, sq.getDBRefs().get(3));
     assertSame(map, dbref4.getMap());
 
     /*
@@ -1121,8 +1216,8 @@ public class SequenceTest
     DBRefEntry dbref6 = new DBRefEntry(dbref2.getSource(), "3",
             dbref2.getAccessionId());
     sq.addDBRef(dbref6);
-    assertEquals(4, sq.getDBRefs().length);
-    assertSame(dbref2, sq.getDBRefs()[1]);
+    assertEquals(4, sq.getDBRefs().size());
+    assertSame(dbref2, sq.getDBRefs().get(1));
     assertEquals("3", dbref2.getVersion());
 
     /*
@@ -1132,8 +1227,8 @@ public class SequenceTest
     DBRefEntry dbref7 = new DBRefEntry(dbref3.getSource(), "3",
             dbref3.getAccessionId());
     sq.addDBRef(dbref7);
-    assertEquals(4, sq.getDBRefs().length);
-    assertSame(dbref3, sq.getDBRefs()[2]);
+    assertEquals(4, sq.getDBRefs().size());
+    assertSame(dbref3, sq.getDBRefs().get(2));
     assertEquals("3", dbref2.getVersion());
   }
 
@@ -1147,7 +1242,7 @@ public class SequenceTest
     assertTrue(primaryDBRefs.isEmpty());
 
     // empty dbrefs
-    sq.setDBRefs(new DBRefEntry[] {});
+       sq.setDBRefs(null);
     primaryDBRefs = sq.getPrimaryDBRefs();
     assertTrue(primaryDBRefs.isEmpty());
 
@@ -1425,21 +1520,23 @@ public class SequenceTest
   public void testFindIndex_withCursor()
   {
     Sequence sq = new Sequence("test/8-13", "-A--BCD-EF--");
+    final int tok = (int) PA.getValue(sq, "changeCount");
+    assertEquals(1, tok);
 
     // find F given A, check cursor is now at the found position
-    assertEquals(10, sq.findIndex(13, new SequenceCursor(sq, 8, 2, 0)));
+    assertEquals(10, sq.findIndex(13, new SequenceCursor(sq, 8, 2, tok)));
     SequenceCursor cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(13, cursor.residuePosition);
     assertEquals(10, cursor.columnPosition);
 
     // find A given F
-    assertEquals(2, sq.findIndex(8, new SequenceCursor(sq, 13, 10, 0)));
+    assertEquals(2, sq.findIndex(8, new SequenceCursor(sq, 13, 10, tok)));
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(8, cursor.residuePosition);
     assertEquals(2, cursor.columnPosition);
 
     // find C given C (no cursor update is done for this case)
-    assertEquals(6, sq.findIndex(10, new SequenceCursor(sq, 10, 6, 0)));
+    assertEquals(6, sq.findIndex(10, new SequenceCursor(sq, 10, 6, tok)));
     SequenceCursor cursor2 = (SequenceCursor) PA.getValue(sq, "cursor");
     assertSame(cursor2, cursor);
 
@@ -1487,41 +1584,48 @@ public class SequenceTest
   public void testFindPosition_withCursor()
   {
     Sequence sq = new Sequence("test/8-13", "-A--BCD-EF--");
+    final int tok = (int) PA.getValue(sq, "changeCount");
+    assertEquals(1, tok);
   
     // find F pos given A - lastCol gets set in cursor
-    assertEquals(13, sq.findPosition(10, new SequenceCursor(sq, 8, 2, 0)));
-    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok0",
+    assertEquals(13,
+            sq.findPosition(10, new SequenceCursor(sq, 8, 2, tok)));
+    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok1",
             PA.getValue(sq, "cursor").toString());
 
     // find A pos given F - first residue column is saved in cursor
-    assertEquals(8, sq.findPosition(2, new SequenceCursor(sq, 13, 10, 0)));
-    assertEquals("test:Pos8:Col2:startCol2:endCol10:tok0",
+    assertEquals(8,
+            sq.findPosition(2, new SequenceCursor(sq, 13, 10, tok)));
+    assertEquals("test:Pos8:Col2:startCol2:endCol10:tok1",
             PA.getValue(sq, "cursor").toString());
   
     // find C pos given C (neither startCol nor endCol is set)
-    assertEquals(10, sq.findPosition(6, new SequenceCursor(sq, 10, 6, 0)));
-    assertEquals("test:Pos10:Col6:startCol0:endCol0:tok0",
+    assertEquals(10,
+            sq.findPosition(6, new SequenceCursor(sq, 10, 6, tok)));
+    assertEquals("test:Pos10:Col6:startCol0:endCol0:tok1",
             PA.getValue(sq, "cursor").toString());
 
     // now the grey area - what residue position for a gapped column? JAL-2562
 
     // find 'residue' for column 3 given cursor for D (so working left)
     // returns B9; cursor is updated to [B 5]
-    assertEquals(9, sq.findPosition(3, new SequenceCursor(sq, 11, 7, 0)));
-    assertEquals("test:Pos9:Col5:startCol0:endCol0:tok0",
+    assertEquals(9, sq.findPosition(3, new SequenceCursor(sq, 11, 7, tok)));
+    assertEquals("test:Pos9:Col5:startCol0:endCol0:tok1",
             PA.getValue(sq, "cursor").toString());
 
     // find 'residue' for column 8 given cursor for D (so working right)
     // returns E12; cursor is updated to [D 7]
-    assertEquals(12, sq.findPosition(8, new SequenceCursor(sq, 11, 7, 0)));
-    assertEquals("test:Pos11:Col7:startCol0:endCol0:tok0",
+    assertEquals(12,
+            sq.findPosition(8, new SequenceCursor(sq, 11, 7, tok)));
+    assertEquals("test:Pos11:Col7:startCol0:endCol0:tok1",
             PA.getValue(sq, "cursor").toString());
 
     // find 'residue' for column 12 given cursor for B
     // returns 1 more than last residue position; cursor is updated to [F 10]
     // lastCol position is saved in cursor
-    assertEquals(14, sq.findPosition(12, new SequenceCursor(sq, 9, 5, 0)));
-    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok0",
+    assertEquals(14,
+            sq.findPosition(12, new SequenceCursor(sq, 9, 5, tok)));
+    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok1",
             PA.getValue(sq, "cursor").toString());
 
     /*
@@ -1529,8 +1633,9 @@ public class SequenceTest
      * returns 1 more than the last residue position
      * cursor is set to last real residue position [F 10]
      */
-    assertEquals(14, sq.findPosition(99, new SequenceCursor(sq, 8, 2, 0)));
-    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok0",
+    assertEquals(14,
+            sq.findPosition(99, new SequenceCursor(sq, 8, 2, tok)));
+    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok1",
             PA.getValue(sq, "cursor").toString());
 
     /*
@@ -1538,14 +1643,14 @@ public class SequenceTest
      */
     sq = new Sequence("test/8-13", "-A--BCD-EF");
     // first find C from A
-    assertEquals(10, sq.findPosition(6, new SequenceCursor(sq, 8, 2, 0)));
+    assertEquals(10, sq.findPosition(6, new SequenceCursor(sq, 8, 2, tok)));
     SequenceCursor cursor = (SequenceCursor) PA.getValue(sq, "cursor");
-    assertEquals("test:Pos10:Col6:startCol0:endCol0:tok0",
+    assertEquals("test:Pos10:Col6:startCol0:endCol0:tok1",
             cursor.toString());
     // now 'find' 99 from C
     // cursor is set to [F 10] and saved lastCol
     assertEquals(14, sq.findPosition(99, cursor));
-    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok0",
+    assertEquals("test:Pos13:Col10:startCol0:endCol10:tok1",
             PA.getValue(sq, "cursor").toString());
   }
 
@@ -1613,6 +1718,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
@@ -1648,6 +1757,17 @@ public class SequenceTest
     // cursor should now be at [F 10]
     cursor = (SequenceCursor) PA.getValue(sq, "cursor");
     assertEquals(new SequenceCursor(sq, 13, 10, ++token), cursor);
+
+    /*
+     * changing sequence start should invalidate cursor
+     */
+    sq = new Sequence("test/8-13", "-A--BCD-EF--");
+    assertEquals(8, sq.getStart());
+    assertEquals(9, sq.findPosition(4)); // B(9)
+    sq.setStart(7);
+    assertEquals(8, sq.findPosition(4)); // is now B(8)
+    sq.setStart(10);
+    assertEquals(11, sq.findPosition(4)); // is now B(11)
   }
 
   @Test(groups = { "Functional" })
@@ -1672,78 +1792,24 @@ public class SequenceTest
   {
     String seqstring = "-A--BCD-EF--";
     SequenceI sq = new Sequence("test/8-13", seqstring);
-    assertEquals(0, PA.getValue(sq, "changeCount"));
+    // changeCount is incremented for setStart
+    assertEquals(1, PA.getValue(sq, "changeCount"));
 
     assertEquals(0, sq.replace('A', 'A')); // same char
     assertEquals(seqstring, sq.getSequenceAsString());
-    assertEquals(0, PA.getValue(sq, "changeCount"));
+    assertEquals(1, PA.getValue(sq, "changeCount"));
 
     assertEquals(0, sq.replace('X', 'Y')); // not there
     assertEquals(seqstring, sq.getSequenceAsString());
-    assertEquals(0, PA.getValue(sq, "changeCount"));
+    assertEquals(1, PA.getValue(sq, "changeCount"));
 
     assertEquals(1, sq.replace('A', 'K'));
     assertEquals("-K--BCD-EF--", sq.getSequenceAsString());
-    assertEquals(1, PA.getValue(sq, "changeCount"));
+    assertEquals(2, PA.getValue(sq, "changeCount"));
 
     assertEquals(6, sq.replace('-', '.'));
     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));
+    assertEquals(3, PA.getValue(sq, "changeCount"));
   }
 
   @Test(groups = { "Functional" })
@@ -2126,4 +2192,25 @@ public class SequenceTest
     assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
 
   }
+  @Test(groups= {"Functional"})
+  public void testTransferAnnotation() {
+    Sequence origSeq = new Sequence("MYSEQ","THISISASEQ");
+    Sequence toSeq = new Sequence("MYSEQ","THISISASEQ");
+    origSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q12345", null, true));
+    toSeq.transferAnnotation(origSeq, null);
+    assertTrue(toSeq.getDBRefs().size()==1);
+    
+    assertTrue(toSeq.getDBRefs().get(0).isCanonical());
+    
+    // check for promotion of non-canonical 
+    // to canonical (e.g. fetch-db-refs on a jalview project pre 2.11.2)
+    toSeq.setDBRefs(null);
+    toSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q12345", null, false));
+    toSeq.transferAnnotation(origSeq, null);
+    assertTrue(toSeq.getDBRefs().size()==1);
+    
+    assertTrue("Promotion of non-canonical DBRefEntry failed",toSeq.getDBRefs().get(0).isCanonical());
+    
+    
+  }
 }