JAL-2362 return correct lower-case colour; Javadoc; unit test
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 12 Dec 2016 12:28:32 +0000 (12:28 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 12 Dec 2016 12:28:32 +0000 (12:28 +0000)
src/jalview/schemes/UserColourScheme.java
test/jalview/schemes/UserColourSchemeTest.java

index 9ae14ca..4bb54f9 100755 (executable)
@@ -99,6 +99,17 @@ public class UserColourScheme extends ResidueColourScheme
     return schemeName;
   }
 
+  /**
+   * Parses a string into a Color, where the accepted formats are
+   * <ul>
+   * <li>an AWT colour name e.g. white</li>
+   * <li>a hex colour value (without prefix) e.g. ff0000</li>
+   * <li>an rgb triple e.g. 100,50,150</li>
+   * </ul>
+   * 
+   * @param colour
+   * @return the parsed colour, or null if parsing fails
+   */
   public static Color getColourFromString(String colour)
   {
     if (colour == null)
@@ -125,19 +136,21 @@ public class UserColourScheme extends ResidueColourScheme
     {
       try
       {
-        java.util.StringTokenizer st = new java.util.StringTokenizer(
-                colour, ",");
-        int r = Integer.parseInt(st.nextToken());
-        int g = Integer.parseInt(st.nextToken());
-        int b = Integer.parseInt(st.nextToken());
-        col = new Color(r, g, b);
+        String[] tokens = colour.split(",");
+        if (tokens.length == 3)
+        {
+          int r = Integer.parseInt(tokens[0].trim());
+          int g = Integer.parseInt(tokens[1].trim());
+          int b = Integer.parseInt(tokens[2].trim());
+          col = new Color(r, g, b);
+        }
       } catch (Exception ex)
       {
+        // non-numeric token or out of 0-255 range
       }
     }
 
     return col;
-
   }
 
   public static Color createColourFromName(String name)
@@ -166,6 +179,21 @@ public class UserColourScheme extends ResidueColourScheme
     return color;
   }
 
+  /**
+   * Parse and save residue colours specified as (for example)
+   * 
+   * <pre>
+   *     D,E=red; K,R,H=0022FF; c=100,50,75
+   * </pre>
+   * 
+   * This should be a semi-colon separated list of colours, which may be defined
+   * by colour name, hex value or comma-separated RGB triple. Each colour is
+   * defined for a comma-separated list of amino acid single letter codes. (Note
+   * that this also allows a colour scheme to be defined for ACGT, but not for
+   * U.)
+   * 
+   * @param paramValue
+   */
   public void parseAppletParameter(String paramValue)
   {
     // TODO: need a function to generate appletParameter colour string from a
@@ -184,16 +212,15 @@ public class UserColourScheme extends ResidueColourScheme
         st2 = new StringTokenizer(residues, " ,");
         while (st2.hasMoreTokens())
         {
-          token = st2.nextToken();
+          String residue = st2.nextToken();
 
-          if (ResidueProperties.aaIndex[token.charAt(0)] == -1)
+          int colIndex = ResidueProperties.aaIndex[residue.charAt(0)];
+          if (colIndex == -1)
           {
             continue;
           }
 
-          int colIndex = ResidueProperties.aaIndex[token.charAt(0)];
-
-          if (token.equalsIgnoreCase("lowerCase"))
+          if (residue.equalsIgnoreCase("lowerCase"))
           {
             if (lowerCaseColours == null)
             {
@@ -210,7 +237,7 @@ public class UserColourScheme extends ResidueColourScheme
             continue;
           }
 
-          if (token.equals(token.toLowerCase()))
+          if (residue.equals(residue.toLowerCase()))
           {
             if (lowerCaseColours == null)
             {
@@ -267,4 +294,23 @@ public class UserColourScheme extends ResidueColourScheme
     lowerCaseColours = lcolours;
   }
 
+  /**
+   * Returns the colour for the given residue character. If the residue is
+   * lower-case, and there is a specific colour defined for lower case, that
+   * colour is returned, else the colour for the upper case residue.
+   */
+  @Override
+  public Color findColour(char c)
+  {
+    if ('a' <= c && c <= 'z' && lowerCaseColours != null)
+    {
+      Color colour = lowerCaseColours[symbolIndex[c]];
+      if (colour != null)
+      {
+        return colour;
+      }
+    }
+    return super.findColour(c);
+  }
+
 }
index 645d5b8..f4875ee 100644 (file)
@@ -57,17 +57,24 @@ public class UserColourSchemeTest
      * by RGB hex code
      */
     String hexColour = Integer.toHexString(Color.RED.getRGB() & 0xffffff);
+    assertEquals("ff0000", hexColour);
     assertEquals(Color.RED, UserColourScheme.getColourFromString(hexColour));
     // 'hex' prefixes _not_ wanted here
     assertNull(UserColourScheme.getColourFromString("0x" + hexColour));
     assertNull(UserColourScheme.getColourFromString("#" + hexColour));
+    // out of range, but Color constructor just or's the rgb value with 0
+    assertEquals(Color.black,
+            UserColourScheme.getColourFromString("1000000"));
 
     /*
      * by RGB triplet
      */
-    String rgb = String.format("%d,%d,%d", Color.red.getRed(),
-            Color.red.getGreen(), Color.red.getBlue());
-    assertEquals(Color.RED, UserColourScheme.getColourFromString(rgb));
+    Color c = Color.pink;
+    String rgb = String.format("%d,%d,%d", c.getRed(), c.getGreen(),
+            c.getBlue());
+    assertEquals("255,175,175", rgb);
+    assertEquals(c, UserColourScheme.getColourFromString(rgb));
+    assertEquals(c, UserColourScheme.getColourFromString("255, 175 , 175"));
 
     /*
      * odds and ends
@@ -77,5 +84,40 @@ public class UserColourSchemeTest
     assertEquals(Color.WHITE, UserColourScheme.getColourFromString("-1"));
     assertNull(UserColourScheme.getColourFromString(String
             .valueOf(Integer.MAX_VALUE)));
+    assertNull(UserColourScheme.getColourFromString("100,200,300"));
+    assertNull(UserColourScheme.getColourFromString("100,200"));
+    assertNull(UserColourScheme.getColourFromString("100,200,100,200"));
+  }
+
+  @Test(groups = "Functional")
+  public void testParseAppletParameter()
+  {
+    UserColourScheme cs = new UserColourScheme("white");
+    cs.parseAppletParameter("D,E=red; K,R,H=0022FF; c=10 , 20,30");
+    assertEquals(Color.RED, cs.findColour('D'));
+    assertEquals(Color.RED, cs.findColour('d'));
+    assertEquals(Color.RED, cs.findColour('E'));
+    assertEquals(Color.RED, cs.findColour('e'));
+    Color c1 = new Color(0x0022ff);
+    assertEquals(c1, cs.findColour('K'));
+    assertEquals(c1, cs.findColour('R'));
+    assertEquals(c1, cs.findColour('h'));
+    Color c2 = new Color(10, 20, 30);
+    assertEquals(c2, cs.findColour('c'));
+
+    cs = new UserColourScheme("white");
+    cs.parseAppletParameter("D,E=red; K,R,H=0022FF; c=10 , 20,30;lowercase=blue;s=pink");
+    assertEquals(Color.RED, cs.findColour('D'));
+    assertEquals(Color.blue, cs.findColour('d'));
+    assertEquals(Color.RED, cs.findColour('E'));
+    assertEquals(Color.blue, cs.findColour('e'));
+    assertEquals(c1, cs.findColour('K'));
+    assertEquals(c1, cs.findColour('R'));
+    assertEquals(Color.blue, cs.findColour('h'));
+    assertEquals(c2, cs.findColour('c'));
+    // 'lowercase' sets all lower-case not already set to the given colour
+    assertEquals(Color.blue, cs.findColour('k'));
+    assertEquals(Color.blue, cs.findColour('a'));
+    assertEquals(Color.pink, cs.findColour('s'));
   }
 }