Merge branch 'documentation/JAL-2325_release2101' into develop
authorJim Procter <jprocter@issues.jalview.org>
Wed, 23 Nov 2016 14:02:29 +0000 (14:02 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 23 Nov 2016 14:02:29 +0000 (14:02 +0000)
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/SequenceFeature.java
src/jalview/gui/AlignmentPanel.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
test/jalview/analysis/scoremodels/FeatureScoreModelTest.java
test/jalview/controller/AlignViewControllerTest.java
test/jalview/datamodel/SequenceFeatureTest.java

index 9451c3b..e9a0dbf 100644 (file)
@@ -243,10 +243,6 @@ public class AlignViewController implements AlignViewControllerI
         SequenceFeature[] sfs = sq.getSequenceFeatures();
         if (sfs != null)
         {
-          /*
-           * check whether the feature start/end (base 1) 
-           * overlaps the selection start/end
-           */
           int ist = sq.findIndex(sq.getStart());
           int iend = sq.findIndex(sq.getEnd());
           if (iend < startPosition || ist > endPosition)
@@ -264,29 +260,54 @@ public class AlignViewController implements AlignViewControllerI
               // - findIndex wastes time by starting from first character and
               // counting
 
-              int i = sq.findIndex(sf.getBegin());
-              int j = sq.findIndex(sf.getEnd());
-              if (j < startPosition || i > endPosition)
+              int sfStartCol = sq.findIndex(sf.getBegin());
+              int sfEndCol = sq.findIndex(sf.getEnd());
+
+              if (sf.isContactFeature())
+              {
+                /*
+                 * 'contact' feature - check for 'start' or 'end'
+                 * position within the selected region
+                 */
+                if (sfStartCol >= startPosition
+                        && sfStartCol <= endPosition)
+                {
+                  bs.set(sfStartCol - 1);
+                  sequenceHasFeature = true;
+                }
+                if (sfEndCol >= startPosition && sfEndCol <= endPosition)
+                {
+                  bs.set(sfEndCol - 1);
+                  sequenceHasFeature = true;
+                }
+                continue;
+              }
+
+              /*
+               * contiguous feature - select feature positions (if any) 
+               * within the selected region
+               */
+              if (sfStartCol > endPosition || sfEndCol < startPosition)
               {
                 // feature is outside selected region
                 continue;
               }
               sequenceHasFeature = true;
-              if (i < startPosition)
+              if (sfStartCol < startPosition)
               {
-                i = startPosition;
+                sfStartCol = startPosition;
               }
-              if (i < ist)
+              if (sfStartCol < ist)
               {
-                i = ist;
+                sfStartCol = ist;
               }
-              if (j > endPosition)
+              if (sfEndCol > endPosition)
               {
-                j = endPosition;
+                sfEndCol = endPosition;
               }
-              for (; i <= j; i++)
+              for (; sfStartCol <= sfEndCol; sfStartCol++)
               {
-                bs.set(i - 1); // convert to base 0
+                bs.set(sfStartCol - 1); // convert to base 0
               }
             }
           }
index 0baa78e..15f54b9 100755 (executable)
@@ -532,4 +532,20 @@ public class SequenceFeature
     return s.hashCode() + getBegin() + getEnd() + (int) getScore()
             + getStrand();
   }
+
+  /**
+   * Answers true if the feature's start/end values represent two related
+   * positions, rather than ends of a range. Such features may be visualised or
+   * reported differently to features on a range.
+   */
+  public boolean isContactFeature()
+  {
+    // TODO abstract one day to a FeatureType class
+    if ("disulfide bond".equalsIgnoreCase(type)
+            || "disulphide bond".equalsIgnoreCase(type))
+    {
+      return true;
+    }
+    return false;
+  }
 }
index dc1f95b..e61b042 100644 (file)
@@ -1299,8 +1299,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (onscreen
             || (idwidth = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null)
     {
-      return (getIdPanel().getWidth() > 0 ? getIdPanel().getWidth()
-              : calculateIdWidth().width + 4);
+      int w = getIdPanel().getWidth();
+      return (w > 0 ? w : calculateIdWidth().width + 4);
     }
     return idwidth.intValue() + 4;
   }
@@ -1448,7 +1448,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   public void makePNGImageMap(File imgMapFile, String imageName)
   {
-    // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
+    // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
     // ////////////////////////////////////////////
     int idWidth = getVisibleIdWidth(false);
     FontMetrics fm = getFontMetrics(av.getFont());
@@ -1462,7 +1462,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
       {
         int s, sSize = av.getAlignment().getHeight(), res, alwidth = av
                 .getAlignment().getWidth(), g, gSize, f, fSize, sy;
-        StringBuffer text = new StringBuffer();
         PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
         out.println(jalview.io.HTMLOutput.getImageMapHTML());
         out.println("<img src=\"" + imageName
@@ -1478,7 +1477,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           SequenceGroup[] groups = av.getAlignment().findAllGroups(seq);
           for (res = 0; res < alwidth; res++)
           {
-            text = new StringBuffer();
+            StringBuilder text = new StringBuilder();
             String triplet = null;
             if (av.getAlignment().isNucleotide())
             {
@@ -1502,18 +1501,20 @@ public class AlignmentPanel extends GAlignmentPanel implements
             {
               if (text.length() < 1)
               {
-                text.append("<area shape=\"rect\" coords=\""
-                        + (idWidth + res * av.getCharWidth()) + "," + sy
-                        + "," + (idWidth + (res + 1) * av.getCharWidth())
-                        + "," + (av.getCharHeight() + sy) + "\""
-                        + " onMouseOver=\"toolTip('" + alIndex + " "
-                        + triplet);
+                text.append("<area shape=\"rect\" coords=\"")
+                        .append((idWidth + res * av.getCharWidth()))
+                        .append(",").append(sy).append(",")
+                        .append((idWidth + (res + 1) * av.getCharWidth()))
+                        .append(",").append((av.getCharHeight() + sy))
+                        .append("\"").append(" onMouseOver=\"toolTip('")
+                        .append(alIndex).append(" ").append(triplet);
               }
 
               if (groups[g].getStartRes() < res
                       && groups[g].getEndRes() > res)
               {
-                text.append("<br><em>" + groups[g].getName() + "</em>");
+                text.append("<br><em>").append(groups[g].getName())
+                        .append("</em>");
               }
             }
 
@@ -1521,12 +1522,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
             {
               if (text.length() < 1)
               {
-                text.append("<area shape=\"rect\" coords=\""
-                        + (idWidth + res * av.getCharWidth()) + "," + sy
-                        + "," + (idWidth + (res + 1) * av.getCharWidth())
-                        + "," + (av.getCharHeight() + sy) + "\""
-                        + " onMouseOver=\"toolTip('" + alIndex + " "
-                        + triplet);
+                text.append("<area shape=\"rect\" coords=\"")
+                        .append((idWidth + res * av.getCharWidth()))
+                        .append(",").append(sy).append(",")
+                        .append((idWidth + (res + 1) * av.getCharWidth()))
+                        .append(",").append((av.getCharHeight() + sy))
+                        .append("\"").append(" onMouseOver=\"toolTip('")
+                        .append(alIndex).append(" ").append(triplet);
               }
               fSize = features.length;
               for (f = 0; f < fSize; f++)
@@ -1535,15 +1537,15 @@ public class AlignmentPanel extends GAlignmentPanel implements
                 if ((features[f].getBegin() <= seq.findPosition(res))
                         && (features[f].getEnd() >= seq.findPosition(res)))
                 {
-                  if (features[f].getType().equals("disulfide bond"))
+                  if (features[f].isContactFeature())
                   {
                     if (features[f].getBegin() == seq.findPosition(res)
                             || features[f].getEnd() == seq
                                     .findPosition(res))
                     {
-                      text.append("<br>disulfide bond "
-                              + features[f].getBegin() + ":"
-                              + features[f].getEnd());
+                      text.append("<br>").append(features[f].getType())
+                              .append(" ").append(features[f].getBegin())
+                              .append(":").append(features[f].getEnd());
                     }
                   }
                   else
@@ -1554,13 +1556,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
                             && !features[f].getType().equals(
                                     features[f].getDescription()))
                     {
-                      text.append(" " + features[f].getDescription());
+                      text.append(" ").append(features[f].getDescription());
                     }
 
                     if (features[f].getValue("status") != null)
                     {
-                      text.append(" (" + features[f].getValue("status")
-                              + ")");
+                      text.append(" (").append(features[f].getValue("status"))
+                              .append(")");
                     }
                   }
                 }
index 850b1dc..6c8f40f 100644 (file)
@@ -141,8 +141,7 @@ public class SequenceAnnotationReport
   void appendFeature(final StringBuilder sb, int rpos,
           Map<String, float[][]> minmax, SequenceFeature feature)
   {
-    String tmpString;
-    if (feature.getType().equals("disulfide bond"))
+    if (feature.isContactFeature())
     {
       if (feature.getBegin() == rpos || feature.getEnd() == rpos)
       {
@@ -150,7 +149,8 @@ public class SequenceAnnotationReport
         {
           sb.append("<br>");
         }
-        sb.append("disulfide bond ").append(feature.getBegin()).append(":")
+        sb.append(feature.getType()).append(" ").append(feature.getBegin())
+                .append(":")
                 .append(feature.getEnd());
       }
     }
@@ -178,7 +178,7 @@ public class SequenceAnnotationReport
         if (feature.getDescription() != null
                 && !feature.description.equals(feature.getType()))
         {
-          tmpString = feature.getDescription();
+          String tmpString = feature.getDescription();
           String tmp2up = tmpString.toUpperCase();
           int startTag = tmp2up.indexOf("<HTML>");
           if (startTag > -1)
@@ -223,11 +223,11 @@ public class SequenceAnnotationReport
         // check score should be shown
         if (!Float.isNaN(feature.getScore()))
         {
-          float[][] rng = (minmax == null) ? null : ((float[][]) minmax
-                  .get(feature.getType()));
+          float[][] rng = (minmax == null) ? null : minmax.get(feature
+                  .getType());
           if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
           {
-            sb.append(" Score=" + feature.getScore());
+            sb.append(" Score=").append(String.valueOf(feature.getScore()));
           }
         }
         String status = (String) feature.getValue("status");
index b007365..9e0089f 100644 (file)
@@ -188,15 +188,20 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
-   * This is used by the Molecule Viewer and Overview to get the accurate colour
-   * of the rendered sequence
+   * This is used by Structure Viewers and the Overview Window to get the
+   * feature colour of the rendered sequence, returned as an RGB value
+   * 
+   * @param defaultColour
+   * @param seq
+   * @param column
+   * @return
    */
-  public synchronized int findFeatureColour(int initialCol,
+  public synchronized int findFeatureColour(int defaultColour,
           final SequenceI seq, int column)
   {
     if (!av.isShowSequenceFeatures())
     {
-      return initialCol;
+      return defaultColour;
     }
 
     SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
@@ -223,7 +228,7 @@ public class FeatureRenderer extends FeatureRendererModel
 
     if (lastSequenceFeatures == null || sfSize == 0)
     {
-      return initialCol;
+      return defaultColour;
     }
 
     if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
@@ -244,7 +249,7 @@ public class FeatureRenderer extends FeatureRendererModel
 
     if (offscreenImage != null)
     {
-      offscreenImage.setRGB(0, 0, initialCol);
+      offscreenImage.setRGB(0, 0, defaultColour);
       drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
 
       return offscreenImage.getRGB(0, 0);
@@ -255,7 +260,7 @@ public class FeatureRenderer extends FeatureRendererModel
 
       if (currentColour == null)
       {
-        return initialCol;
+        return defaultColour;
       }
       else
       {
@@ -275,6 +280,19 @@ public class FeatureRenderer extends FeatureRendererModel
 
   int epos;
 
+  /**
+   * Draws the sequence on the graphics context, or just determines the colour
+   * that would be drawn (if flag offscreenrender is true).
+   * 
+   * @param g
+   * @param seq
+   * @param start
+   *          start column (or sequence position in offscreenrender mode)
+   * @param end
+   *          end column (not used in offscreenrender mode)
+   * @param y1
+   *          vertical offset at which to draw on the graphics
+   */
   public synchronized void drawSequence(Graphics g, final SequenceI seq,
           int start, int end, int y1)
   {
@@ -312,12 +330,10 @@ public class FeatureRenderer extends FeatureRendererModel
     }
 
     sfSize = lastSequenceFeatures.length;
-    String type;
     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
     {
-      type = renderOrder[renderIndex];
-
-      if (type == null || !showFeatureOfType(type))
+      String type = renderOrder[renderIndex];
+      if (!showFeatureOfType(type))
       {
         continue;
       }
@@ -332,16 +348,16 @@ public class FeatureRenderer extends FeatureRendererModel
           continue;
         }
 
-        if (featureGroups != null
-                && sequenceFeature.featureGroup != null
-                && sequenceFeature.featureGroup.length() != 0
-                && featureGroups.containsKey(sequenceFeature.featureGroup)
-                && !featureGroups.get(sequenceFeature.featureGroup)
-                        .booleanValue())
+        if (featureGroupNotShown(sequenceFeature))
         {
           continue;
         }
 
+        /*
+         * check feature overlaps the visible part of the alignment, 
+         * unless doing offscreenRender (to the Overview window or a 
+         * structure viewer) which is not limited 
+         */
         if (!offscreenRender
                 && (sequenceFeature.getBegin() > epos || sequenceFeature
                         .getEnd() < spos))
@@ -349,35 +365,43 @@ public class FeatureRenderer extends FeatureRendererModel
           continue;
         }
 
+        Color featureColour = getColour(sequenceFeature);
+        boolean isContactFeature = sequenceFeature.isContactFeature();
+
         if (offscreenRender && offscreenImage == null)
         {
-          if (sequenceFeature.begin <= start
-                  && sequenceFeature.end >= start)
+          /*
+           * offscreen mode with no image (image is only needed if transparency 
+           * is applied to feature colours) - just check feature is rendered at 
+           * the requested position (start == sequence position in this mode)
+           */
+          boolean featureIsAtPosition = sequenceFeature.begin <= start
+                  && sequenceFeature.end >= start;
+          if (isContactFeature)
+          {
+            featureIsAtPosition = sequenceFeature.begin == start
+                    || sequenceFeature.end == start;
+          }
+          if (featureIsAtPosition)
           {
             // this is passed out to the overview and other sequence renderers
             // (e.g. molecule viewer) to get displayed colour for rendered
             // sequence
-            currentColour = new Integer(getColour(sequenceFeature).getRGB());
+            currentColour = new Integer(featureColour.getRGB());
             // used to be retreived from av.featuresDisplayed
             // currentColour = av.featuresDisplayed
             // .get(sequenceFeatures[sfindex].type);
 
           }
         }
-        else if (sequenceFeature.type.equals("disulfide bond"))
+        else if (isContactFeature)
         {
           renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
-                  seq.findIndex(sequenceFeature.begin) - 1,
-                  getColour(sequenceFeature)
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
+                  seq.findIndex(sequenceFeature.begin) - 1, featureColour,
+                  start, end, y1);
           renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
-                  seq.findIndex(sequenceFeature.end) - 1,
-                  getColour(sequenceFeature)
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
+                  seq.findIndex(sequenceFeature.end) - 1, featureColour,
+                  start, end, y1);
 
         }
         else if (showFeature(sequenceFeature))
@@ -388,19 +412,17 @@ public class FeatureRenderer extends FeatureRendererModel
             renderScoreFeature(g, seq,
                     seq.findIndex(sequenceFeature.begin) - 1,
                     seq.findIndex(sequenceFeature.end) - 1,
-                    getColour(sequenceFeature), start, end, y1,
+                    featureColour, start, end, y1,
                     normaliseScore(sequenceFeature));
           }
           else
           {
             renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
                     seq.findIndex(sequenceFeature.end) - 1,
-                    getColour(sequenceFeature), start, end, y1);
+                    featureColour, start, end, y1);
           }
         }
-
       }
-
     }
 
     if (transparency != 1.0f && g != null)
@@ -412,6 +434,24 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
+   * Answers true if the feature belongs to a feature group which is not
+   * currently displayed, else false
+   * 
+   * @param sequenceFeature
+   * @return
+   */
+  protected boolean featureGroupNotShown(
+          final SequenceFeature sequenceFeature)
+  {
+    return featureGroups != null
+            && sequenceFeature.featureGroup != null
+            && sequenceFeature.featureGroup.length() != 0
+            && featureGroups.containsKey(sequenceFeature.featureGroup)
+            && !featureGroups.get(sequenceFeature.featureGroup)
+                    .booleanValue();
+  }
+
+  /**
    * Called when alignment in associated view has new/modified features to
    * discover and display.
    * 
index 4ac4804..c1ad465 100644 (file)
@@ -288,8 +288,12 @@ public abstract class FeatureRendererModel implements
           continue;
         }
 
-        if ((features[i].getBegin() <= res)
-                && (features[i].getEnd() >= res))
+        // check if start/end are at res, and if not a contact feature, that res
+        // lies between start and end
+        if ((features[i].getBegin() == res || features[i].getEnd() == res)
+                || (!features[i].isContactFeature()
+                        && (features[i].getBegin() < res) && (features[i]
+                        .getEnd() >= res)))
         {
           tmp.add(features[i]);
         }
@@ -564,9 +568,16 @@ public abstract class FeatureRendererModel implements
     return fc.isColored(sequenceFeature);
   }
 
+  /**
+   * Answers true if the feature type is currently selected to be displayed,
+   * else false
+   * 
+   * @param type
+   * @return
+   */
   protected boolean showFeatureOfType(String type)
   {
-    return av.getFeaturesDisplayed().isVisible(type);
+    return type == null ? false : av.getFeaturesDisplayed().isVisible(type);
   }
 
   @Override
index 309790f..292b576 100644 (file)
@@ -27,6 +27,8 @@ import jalview.gui.AlignFrame;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 
+import java.util.Arrays;
+
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -137,4 +139,45 @@ public class FeatureScoreModelTest
               + "(" + s + ") should still be distinct from FER1_MAIZE (3)");
     }
   }
+
+  /**
+   * Check findFeatureAt doesn't return contact features except at contact
+   * points TODO:move to under the FeatureRendererModel test suite
+   */
+  @Test(groups = { "Functional" })
+  public void testFindFeatureAt_PointFeature() throws Exception
+  {
+    String alignment = "a CCCCCCGGGGGGCCCCCC\n" + "b CCCCCCGGGGGGCCCCCC\n"
+            + "c CCCCCCGGGGGGCCCCCC\n";
+    AlignFrame af = new jalview.io.FileLoader(false)
+            .LoadFileWaitTillLoaded(alignment, FormatAdapter.PASTE);
+    SequenceI aseq = af.getViewport().getAlignment().getSequenceAt(0);
+    SequenceFeature sf = null;
+    sf = new SequenceFeature("disulphide bond", "", 2, 5, Float.NaN, "");
+    aseq.addSequenceFeature(sf);
+    Assert.assertTrue(sf.isContactFeature());
+    af.refreshFeatureUI(true);
+    af.getFeatureRenderer().setAllVisible(Arrays.asList("disulphide bond"));
+    Assert.assertEquals(af.getFeatureRenderer().getDisplayedFeatureTypes()
+            .size(), 1, "Should be just one feature type displayed");
+    // step through and check for pointwise feature presence/absence
+    Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 1)
+            .size(), 0);
+    // step through and check for pointwise feature presence/absence
+    Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 2)
+            .size(), 1);
+    // step through and check for pointwise feature presence/absence
+    Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 3)
+            .size(), 0);
+    // step through and check for pointwise feature presence/absence
+    Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 4)
+            .size(), 0);
+    // step through and check for pointwise feature presence/absence
+    Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 5)
+            .size(), 1);
+    // step through and check for pointwise feature presence/absence
+    Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtRes(aseq, 6)
+            .size(), 0);
+  }
+
 }
index 53eb171..e2c2130 100644 (file)
@@ -45,10 +45,10 @@ public class AlignViewControllerTest
   @Test(groups = "Functional")
   public void testFindColumnsWithFeature()
   {
-    SequenceI seq1 = new Sequence("seq1", "aMMMaaaaaaaaaaaaaaaa");
-    SequenceI seq2 = new Sequence("seq2", "aaaMMMMMMMaaaaaaaaaa");
-    SequenceI seq3 = new Sequence("seq3", "aaaaaaaaaaMMMMMaaaaa");
-    SequenceI seq4 = new Sequence("seq3", "aaaaaaaaaaaaaaaaaaaa");
+    SequenceI seq1 = new Sequence("seq1", "-a-MMMaaaaaaaaaaaaaaaa");
+    SequenceI seq2 = new Sequence("seq2", "aa--aMM-MMMMMaaaaaaaaaa");
+    SequenceI seq3 = new Sequence("seq3", "abcab-caD-aaMMMMMaaaaa");
+    SequenceI seq4 = new Sequence("seq4", "abc--abcaaaaaaaaaaaaaa");
 
     /*
      * features start/end are base 1
@@ -61,13 +61,16 @@ public class AlignViewControllerTest
             null));
     seq3.addSequenceFeature(new SequenceFeature("Metal", "desc", 11, 15,
             0f, null));
+    // disulfide bond is a 'contact feature' - only select its 'start' and 'end'
+    seq3.addSequenceFeature(new SequenceFeature("disulfide bond", "desc", 8, 12,
+            0f, null));
 
     /*
-     * select the first three columns --> Metal in seq1 2-3
+     * select the first five columns --> Metal in seq1 cols 4-5
      */
     SequenceGroup sg = new SequenceGroup();
     sg.setStartRes(0); // base 0
-    sg.setEndRes(2);
+    sg.setEndRes(4);
     sg.addSequence(seq1, false);
     sg.addSequence(seq2, false);
     sg.addSequence(seq3, false);
@@ -78,36 +81,37 @@ public class AlignViewControllerTest
             bs);
     assertEquals(1, seqCount);
     assertEquals(2, bs.cardinality());
-    assertTrue(bs.get(1));
-    assertTrue(bs.get(2));
+    assertTrue(bs.get(3)); // base 0
+    assertTrue(bs.get(4));
 
     /*
-     * select the first four columns: Metal in seq1 2:4, seq2 4:4
+     * select the first seven columns: Metal in seq1 cols 4-6, seq2 cols 6-7 
      */
-    sg.setEndRes(3);
+    sg.setEndRes(6);
     bs.clear();
     seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, bs);
     assertEquals(2, seqCount);
-    assertEquals(3, bs.cardinality());
-    assertTrue(bs.get(1));
-    assertTrue(bs.get(2));
+    assertEquals(4, bs.cardinality());
     assertTrue(bs.get(3));
+    assertTrue(bs.get(4));
+    assertTrue(bs.get(5));
+    assertTrue(bs.get(6));
 
     /*
-     * select column 11: Metal in seq3 only
+     * select column 14: Metal in seq3 only
      */
-    sg.setStartRes(10);
-    sg.setEndRes(10);
+    sg.setStartRes(13);
+    sg.setEndRes(13);
     bs.clear();
     seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, bs);
     assertEquals(1, seqCount);
     assertEquals(1, bs.cardinality());
-    assertTrue(bs.get(10));
+    assertTrue(bs.get(13));
 
     /*
-     * select columns 16-20: no Metal feature
+     * select columns 18-20: no Metal feature
      */
-    sg.setStartRes(15);
+    sg.setStartRes(17);
     sg.setEndRes(19);
     bs.clear();
     seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, bs);
@@ -115,6 +119,30 @@ public class AlignViewControllerTest
     assertEquals(0, bs.cardinality());
 
     /*
+     * columns 11-13 should not match disulfide bond at 8/12
+     */
+    sg.setStartRes(10);
+    sg.setEndRes(12);
+    bs.clear();
+    seqCount = AlignViewController.findColumnsWithFeature("disulfide bond",
+            sg, bs);
+    assertEquals(0, seqCount);
+    assertEquals(0, bs.cardinality());
+
+    /*
+     * columns 6-18 should match disulfide bond at columns 9, 14
+     */
+    sg.setStartRes(5);
+    sg.setEndRes(17);
+    bs.clear();
+    seqCount = AlignViewController.findColumnsWithFeature("disulfide bond",
+            sg, bs);
+    assertEquals(1, seqCount);
+    assertEquals(2, bs.cardinality());
+    assertTrue(bs.get(8));
+    assertTrue(bs.get(13));
+
+    /*
      * look for a feature that isn't there
      */
     sg.setStartRes(0);
index 5150337..23812ea 100644 (file)
@@ -197,4 +197,24 @@ public class SequenceFeatureTest
     sf1.setStatus("new");
     assertTrue(sf1.equals(sf2));
   }
+
+  @Test(groups = { "Functional" })
+  public void testIsContactFeature()
+  {
+    SequenceFeature sf = new SequenceFeature("type", "desc", 22, 33, 12.5f,
+            "group");
+    assertFalse(sf.isContactFeature());
+    sf.setType("");
+    assertFalse(sf.isContactFeature());
+    sf.setType(null);
+    assertFalse(sf.isContactFeature());
+    sf.setType("Disulfide Bond");
+    assertTrue(sf.isContactFeature());
+    sf.setType("disulfide bond");
+    assertTrue(sf.isContactFeature());
+    sf.setType("Disulphide Bond");
+    assertTrue(sf.isContactFeature());
+    sf.setType("disulphide bond");
+    assertTrue(sf.isContactFeature());
+  }
 }