* @param max
*/
void updateBounds(float min, float max);
+
+ /**
+ * Returns the colour in Jalview features file format
+ *
+ * @return
+ */
+ String toJalviewFormat(String featureType);
}
import jalview.datamodel.SequenceDummy;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
-import jalview.schemes.AnnotationColourGradient;
import jalview.schemes.FeatureColour;
import jalview.schemes.UserColourScheme;
-import jalview.util.Format;
import jalview.util.MapList;
import java.io.IOException;
* GFF3 file
*/
ArrayList<SequenceI> newseqs = new ArrayList<SequenceI>();
- String type, desc, token = null;
+ String theType, desc, token = null;
- int index, start, end;
- float score;
StringTokenizer st;
SequenceFeature sf;
String featureGroup = null, groupLink = null;
- Map typeLink = new Hashtable();
+ Map<String, String> typeLink = new Hashtable<String, String>();
/**
* when true, assume GFF style features rather than Jalview style.
*/
Map<String, String> gffProps = new HashMap<String, String>();
while ((line = nextLine()) != null)
{
+ int featureStart, featureEnd;
// skip comments/process pragmas
if (line.startsWith("#"))
{
if (st.countTokens() > 1 && st.countTokens() < 4)
{
GFFFile = false;
- type = st.nextToken();
- if (type.equalsIgnoreCase("startgroup"))
+ theType = st.nextToken();
+ if (theType.equalsIgnoreCase("startgroup"))
{
featureGroup = st.nextToken();
if (st.hasMoreElements())
featureLink.put(featureGroup, groupLink);
}
}
- else if (type.equalsIgnoreCase("endgroup"))
+ else if (theType.equalsIgnoreCase("endgroup"))
{
// We should check whether this is the current group,
// but at present theres no way of showing more than 1 group
}
else
{
- FeatureColourI colour = null;
String colscheme = st.nextToken();
- if (colscheme.indexOf("|") > -1
- || colscheme.trim().equalsIgnoreCase("label"))
+ try
{
- // Parse '|' separated graduated colourscheme fields:
- // [label|][mincolour|maxcolour|[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue]
- // can either provide 'label' only, first is optional, next two
- // colors are required (but may be
- // left blank), next is optional, nxt two min/max are required.
- // first is either 'label'
- // first/second and third are both hexadecimal or word equivalent
- // colour.
- // next two are values parsed as floats.
- // fifth is either 'above','below', or 'none'.
- // sixth is a float value and only required when fifth is either
- // 'above' or 'below'.
- StringTokenizer gcol = new StringTokenizer(colscheme, "|",
- true);
- // set defaults
- int threshtype = AnnotationColourGradient.NO_THRESHOLD;
- float min = Float.MIN_VALUE, max = Float.MAX_VALUE, threshval = Float.NaN;
- boolean labelCol = false;
- // Parse spec line
- String mincol = gcol.nextToken();
- if (mincol == "|")
- {
- System.err
- .println("Expected either 'label' or a colour specification in the line: "
- + line);
- continue;
- }
- String maxcol = null;
- if (mincol.toLowerCase().indexOf("label") == 0)
- {
- labelCol = true;
- mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null); // skip
- // '|'
- mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
- }
- String abso = null, minval, maxval;
- if (mincol != null)
- {
- // at least four more tokens
- if (mincol.equals("|"))
- {
- mincol = "";
- }
- else
- {
- gcol.nextToken(); // skip next '|'
- }
- // continue parsing rest of line
- maxcol = gcol.nextToken();
- if (maxcol.equals("|"))
- {
- maxcol = "";
- }
- else
- {
- gcol.nextToken(); // skip next '|'
- }
- abso = gcol.nextToken();
- gcol.nextToken(); // skip next '|'
- if (abso.toLowerCase().indexOf("abso") != 0)
- {
- minval = abso;
- abso = null;
- }
- else
- {
- minval = gcol.nextToken();
- gcol.nextToken(); // skip next '|'
- }
- maxval = gcol.nextToken();
- if (gcol.hasMoreTokens())
- {
- gcol.nextToken(); // skip next '|'
- }
- try
- {
- if (minval.length() > 0)
- {
- min = new Float(minval).floatValue();
- }
- } catch (Exception e)
- {
- System.err
- .println("Couldn't parse the minimum value for graduated colour for type ("
- + colscheme
- + ") - did you misspell 'auto' for the optional automatic colour switch ?");
- e.printStackTrace();
- }
- try
- {
- if (maxval.length() > 0)
- {
- max = new Float(maxval).floatValue();
- }
- } catch (Exception e)
- {
- System.err
- .println("Couldn't parse the maximum value for graduated colour for type ("
- + colscheme + ")");
- e.printStackTrace();
- }
- }
- else
- {
- // add in some dummy min/max colours for the label-only
- // colourscheme.
- mincol = "FFFFFF";
- maxcol = "000000";
- }
- try
- {
- colour = new FeatureColour(
- new UserColourScheme(mincol).findColour('A'),
- new UserColourScheme(maxcol).findColour('A'), min,
- max);
- } catch (Exception e)
+ FeatureColourI colour = FeatureColour
+ .parseJalviewFeatureColour(colscheme);
+ if (colour != null)
{
- System.err
- .println("Couldn't parse the graduated colour scheme ("
- + colscheme + ")");
- e.printStackTrace();
+ colours.put(theType, colour);
}
- if (colour != null)
+ if (st.hasMoreElements())
{
- colour.setColourByLabel(labelCol);
- colour.setAutoScaled(abso == null);
- // add in any additional parameters
- String ttype = null, tval = null;
- if (gcol.hasMoreTokens())
- {
- // threshold type and possibly a threshold value
- ttype = gcol.nextToken();
- if (ttype.toLowerCase().startsWith("below"))
- {
- colour.setBelowThreshold(true);
- }
- else if (ttype.toLowerCase().startsWith("above"))
- {
- colour.setAboveThreshold(true);
- }
- else
- {
- if (!ttype.toLowerCase().startsWith("no"))
- {
- System.err
- .println("Ignoring unrecognised threshold type : "
- + ttype);
- }
- }
- }
- if (colour.hasThreshold())
+ String link = st.nextToken();
+ typeLink.put(theType, link);
+ if (featureLink == null)
{
- try
- {
- gcol.nextToken();
- tval = gcol.nextToken();
- colour.setThreshold(new Float(tval).floatValue());
- } catch (Exception e)
- {
- System.err
- .println("Couldn't parse threshold value as a float: ("
- + tval + ")");
- e.printStackTrace();
- }
- }
- // parse the thresh-is-min token ?
- if (gcol.hasMoreTokens())
- {
- System.err
- .println("Ignoring additional tokens in parameters in graduated colour specification\n");
- while (gcol.hasMoreTokens())
- {
- System.err.println("|" + gcol.nextToken());
- }
- System.err.println("\n");
+ featureLink = new Hashtable();
}
+ featureLink.put(theType, link);
}
- }
- else
+ } catch (IllegalArgumentException e)
{
- UserColourScheme ucs = new UserColourScheme(colscheme);
- colour = new FeatureColour(ucs.findColour('A'));
- }
- if (colour != null)
- {
- colours.put(type, colour);
- }
- if (st.hasMoreElements())
- {
- String link = st.nextToken();
- typeLink.put(type, link);
- if (featureLink == null)
- {
- featureLink = new Hashtable();
- }
- featureLink.put(type, link);
+ System.err.println("Error parsing feature colour scheme "
+ + colscheme + " : " + e.getMessage());
}
}
continue;
// could also be a source term rather than description line
group = new String(desc);
}
- type = st.nextToken();
+ theType = st.nextToken();
try
{
String stt = st.nextToken();
if (stt.length() == 0 || stt.equals("-"))
{
- start = 0;
+ featureStart = 0;
}
else
{
- start = Integer.parseInt(stt);
+ featureStart = Integer.parseInt(stt);
}
} catch (NumberFormatException ex)
{
- start = 0;
+ featureStart = 0;
}
try
{
String stt = st.nextToken();
if (stt.length() == 0 || stt.equals("-"))
{
- end = 0;
+ featureEnd = 0;
}
else
{
- end = Integer.parseInt(stt);
+ featureEnd = Integer.parseInt(stt);
}
} catch (NumberFormatException ex)
{
- end = 0;
+ featureEnd = 0;
}
// TODO: decide if non positional feature assertion for input data
// where end==0 is generally valid
- if (end == 0)
+ if (featureEnd == 0)
{
// treat as non-positional feature, regardless.
- start = 0;
+ featureStart = 0;
}
+ float score = 0f;
try
{
score = new Float(st.nextToken()).floatValue();
} catch (NumberFormatException ex)
{
- score = 0;
+ // ignore
}
- sf = new SequenceFeature(type, desc, start, end, score, group);
+ sf = new SequenceFeature(theType, desc, featureStart, featureEnd, score, group);
try
{
if (st.hasMoreTokens())
{
- StringBuffer attributes = new StringBuffer();
+ StringBuilder attributes = new StringBuilder();
boolean sep = false;
while (st.hasMoreTokens())
{
- attributes.append((sep ? "\t" : "") + st.nextElement());
+ attributes.append(sep ? "\t" : "").append(
+ st.nextElement());
sep = true;
}
// TODO validate and split GFF2 attributes field ? parse out
seqId = null;
try
{
- index = Integer.parseInt(st.nextToken());
- seq = align.getSequenceAt(index);
+ int idx = Integer.parseInt(st.nextToken());
+ seq = align.getSequenceAt(idx);
} catch (NumberFormatException ex)
{
seq = null;
break;
}
- start = Integer.parseInt(st.nextToken());
- end = Integer.parseInt(st.nextToken());
+ featureStart = Integer.parseInt(st.nextToken());
+ featureEnd = Integer.parseInt(st.nextToken());
- type = st.nextToken();
+ theType = st.nextToken();
- if (!colours.containsKey(type))
+ if (!colours.containsKey(theType))
{
// Probably the old style groups file
- UserColourScheme ucs = new UserColourScheme(type);
- colours.put(type, ucs.findColour('A'));
+ colours.put(
+ theType,
+ new FeatureColour(UserColourScheme
+ .getColourFromString(theType)));
}
- sf = new SequenceFeature(type, desc, "", start, end, featureGroup);
+ sf = new SequenceFeature(theType, desc, "", featureStart, featureEnd, featureGroup);
if (st.hasMoreTokens())
{
+ float score = 0f;
try
{
score = new Float(st.nextToken()).floatValue();
// update colourgradient bounds if allowed to
} catch (NumberFormatException ex)
{
- score = 0;
+ // ignore
}
sf.setScore(score);
}
sf.addLink(groupLink);
sf.description += "%LINK%";
}
- if (typeLink.containsKey(type) && removeHTML)
+ if (typeLink.containsKey(theType) && removeHTML)
{
- sf.addLink(typeLink.get(type).toString());
+ sf.addLink(typeLink.get(theType));
sf.description += "%LINK%";
}
Map<String, FeatureColourI> visible,
boolean visOnly, boolean nonpos)
{
- StringBuffer out = new StringBuffer();
- SequenceFeature[] next;
- boolean featuresGen = false;
if (visOnly && !nonpos && (visible == null || visible.size() < 1))
{
// no point continuing.
return "No Features Visible";
}
+ StringBuilder out = new StringBuilder(128);
+ SequenceFeature[] next;
+ boolean featuresGen = false;
if (visible != null && visOnly)
{
// viewed features
// TODO: decide if feature links should also be written here ?
Iterator<String> en = visible.keySet().iterator();
- String feature, color;
while (en.hasNext())
{
- feature = en.next();
-
- FeatureColourI gc = visible.get(feature);
- if (!gc.isSimpleColour())
- {
- color = (gc.isColourByLabel() ? "label|" : "")
- + Format.getHexString(gc.getMinColour()) + "|"
- + Format.getHexString(gc.getMaxColour())
- + (gc.isAutoScaled() ? "|" : "|abso|") + gc.getMin()
- + "|"
- + gc.getMax() + "|";
- if (gc.isBelowThreshold())
- {
- color += "below|" + gc.getThreshold();
- }
- else if (gc.isAboveThreshold())
- {
- color += "above|" + gc.getThreshold();
- }
- else
- {
- color += "none";
- }
- }
- else
- {
- color = Format.getHexString(gc.getColour());
- }
- // else
- // {
- // // legacy support for integer objects containing colour triplet
- // values
- // color = Format.getHexString(new java.awt.Color(Integer
- // .parseInt(visible.get(type).toString())));
- // }
- out.append(feature);
- out.append("\t");
- out.append(color);
- out.append(newline);
+ String featureType = en.next();
+ FeatureColourI colour = visible.get(featureType);
+ out.append(colour.toJalviewFormat(featureType)).append(newline);
}
}
// Work out which groups are both present and visible
import jalview.api.FeatureColourI;
import jalview.datamodel.SequenceFeature;
+import jalview.util.Format;
import java.awt.Color;
+import java.util.StringTokenizer;
/**
* A class that wraps either a simple colour or a graduated colour
*/
public class FeatureColour implements FeatureColourI
{
+ private static final String BAR = "|";
+
final private Color colour;
final private Color minColour;
final private float deltaBlue;
/**
+ * Parses a Jalview features file format colour descriptor
+ * [label|][mincolour|maxcolour
+ * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples:
+ * <ul>
+ * <li>red</li>
+ * <li>a28bbb</li>
+ * <li>25,125,213</li>
+ * <li>label</li>
+ * <li>label|||0.0|0.0|above|12.5</li>
+ * <li>label|||0.0|0.0|below|12.5</li>
+ * <li>red|green|12.0|26.0|none</li>
+ * <li>a28bbb|3eb555|12.0|26.0|above|12.5</li>
+ * <li>a28bbb|3eb555|abso|12.0|26.0|below|12.5</li>
+ * </ul>
+ *
+ * @param descriptor
+ * @return
+ * @throws IllegalArgumentException
+ * if not parseable
+ */
+ public static FeatureColour parseJalviewFeatureColour(String descriptor)
+ {
+ StringTokenizer gcol = new StringTokenizer(descriptor, "|", true);
+ float min = Float.MIN_VALUE;
+ float max = Float.MAX_VALUE;
+ boolean labelColour = false;
+
+ String mincol = gcol.nextToken();
+ if (mincol == "|")
+ {
+ throw new IllegalArgumentException(
+ "Expected either 'label' or a colour specification in the line: "
+ + descriptor);
+ }
+ String maxcol = null;
+ if (mincol.toLowerCase().indexOf("label") == 0)
+ {
+ labelColour = true;
+ mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
+ // skip '|'
+ mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
+ }
+
+ if (!labelColour && !gcol.hasMoreTokens())
+ {
+ /*
+ * only a simple colour specification - parse it
+ */
+ Color colour = UserColourScheme.getColourFromString(descriptor);
+ if (colour == null)
+ {
+ throw new IllegalArgumentException("Invalid colour descriptor: "
+ + descriptor);
+ }
+ return new FeatureColour(colour);
+ }
+
+ /*
+ * autoScaled == true: colours range over actual score range; autoScaled ==
+ * false ('abso'): colours range over min/max range
+ */
+ boolean autoScaled = false;
+ String tok = null, minval, maxval;
+ if (mincol != null)
+ {
+ // at least four more tokens
+ if (mincol.equals("|"))
+ {
+ mincol = "";
+ }
+ else
+ {
+ gcol.nextToken(); // skip next '|'
+ }
+ maxcol = gcol.nextToken();
+ if (maxcol.equals("|"))
+ {
+ maxcol = "";
+ }
+ else
+ {
+ gcol.nextToken(); // skip next '|'
+ }
+ tok = gcol.nextToken();
+ gcol.nextToken(); // skip next '|'
+ if (tok.toLowerCase().indexOf("abso") != 0)
+ {
+ minval = tok;
+ autoScaled = true;
+ }
+ else
+ {
+ minval = gcol.nextToken();
+ gcol.nextToken(); // skip next '|'
+ }
+ maxval = gcol.nextToken();
+ if (gcol.hasMoreTokens())
+ {
+ gcol.nextToken(); // skip next '|'
+ }
+ try
+ {
+ if (minval.length() > 0)
+ {
+ min = new Float(minval).floatValue();
+ }
+ } catch (Exception e)
+ {
+ throw new IllegalArgumentException(
+ "Couldn't parse the minimum value for graduated colour ("
+ + descriptor + ")");
+ }
+ try
+ {
+ if (maxval.length() > 0)
+ {
+ max = new Float(maxval).floatValue();
+ }
+ } catch (Exception e)
+ {
+ throw new IllegalArgumentException(
+ "Couldn't parse the maximum value for graduated colour ("
+ + descriptor + ")");
+ }
+ }
+ else
+ {
+ // add in some dummy min/max colours for the label-only
+ // colourscheme.
+ mincol = "FFFFFF";
+ maxcol = "000000";
+ }
+
+ /*
+ * construct the FeatureColour
+ */
+ FeatureColour featureColour;
+ try
+ {
+ featureColour = new FeatureColour(
+ new UserColourScheme(mincol).findColour('A'),
+ new UserColourScheme(maxcol).findColour('A'), min, max);
+ featureColour.setColourByLabel(labelColour);
+ featureColour.setAutoScaled(autoScaled);
+ // add in any additional parameters
+ String ttype = null, tval = null;
+ if (gcol.hasMoreTokens())
+ {
+ // threshold type and possibly a threshold value
+ ttype = gcol.nextToken();
+ if (ttype.toLowerCase().startsWith("below"))
+ {
+ featureColour.setBelowThreshold(true);
+ }
+ else if (ttype.toLowerCase().startsWith("above"))
+ {
+ featureColour.setAboveThreshold(true);
+ }
+ else
+ {
+ if (!ttype.toLowerCase().startsWith("no"))
+ {
+ System.err.println("Ignoring unrecognised threshold type : "
+ + ttype);
+ }
+ }
+ }
+ if (featureColour.hasThreshold())
+ {
+ try
+ {
+ gcol.nextToken();
+ tval = gcol.nextToken();
+ featureColour.setThreshold(new Float(tval).floatValue());
+ } catch (Exception e)
+ {
+ System.err.println("Couldn't parse threshold value as a float: ("
+ + tval + ")");
+ }
+ }
+ if (gcol.hasMoreTokens())
+ {
+ System.err
+ .println("Ignoring additional tokens in parameters in graduated colour specification\n");
+ while (gcol.hasMoreTokens())
+ {
+ System.err.println("|" + gcol.nextToken());
+ }
+ System.err.println("\n");
+ }
+ return featureColour;
+ } catch (Exception e)
+ {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ /**
* Default constructor
*/
public FeatureColour()
*/
public FeatureColour(Color c)
{
- minColour = null;
- maxColour = null;
+ minColour = Color.WHITE;
+ maxColour = Color.BLACK;
minRed = 0f;
minGreen = 0f;
minBlue = 0f;
return isAboveThreshold() || isBelowThreshold();
}
+ @Override
+ public String toJalviewFormat(String featureType)
+ {
+ String colourString = null;
+ if (isSimpleColour())
+ {
+ colourString = Format.getHexString(getColour());
+ }
+ else
+ {
+ StringBuilder sb = new StringBuilder(32);
+ if (isColourByLabel())
+ {
+ sb.append("label");
+ if (hasThreshold())
+ {
+ sb.append(BAR).append(BAR).append(BAR);
+ }
+ }
+ if (isGraduatedColour())
+ {
+ sb.append(Format.getHexString(getMinColour())).append(BAR);
+ sb.append(Format.getHexString(getMaxColour())).append(BAR);
+ if (isAutoScaled())
+ {
+ sb.append("abso").append(BAR);
+ }
+ }
+ if (hasThreshold() || isGraduatedColour())
+ {
+ sb.append(getMin()).append(BAR);
+ sb.append(getMax()).append(BAR);
+ if (isBelowThreshold())
+ {
+ sb.append("below").append(BAR).append(getThreshold());
+ }
+ else if (isAboveThreshold())
+ {
+ sb.append("above").append(BAR).append(getThreshold());
+ }
+ else
+ {
+ sb.append("none");
+ }
+ }
+ colourString = sb.toString();
+ }
+ return String.format("%s\t%s", featureType, colourString);
+ }
+
}
public static Color getColourFromString(String colour)
{
+ if (colour == null)
+ {
+ return null;
+ }
colour = colour.trim();
Color col = null;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.fail;
import jalview.datamodel.SequenceFeature;
+import jalview.util.Format;
import java.awt.Color;
assertFalse(fc.isColored(sf));
assertEquals(new Color(204, 204, 204), fc.getColor(sf));
}
+
+ /**
+ * Test output of feature colours to Jalview features file format
+ */
+ @Test(groups = { "Functional" })
+ public void testToJalviewFormat()
+ {
+ /*
+ * plain colour - to RGB hex code
+ */
+ FeatureColour fc = new FeatureColour(Color.RED);
+ String redHex = Format.getHexString(Color.RED);
+ String hexColour = redHex;
+ assertEquals("domain\t" + hexColour, fc.toJalviewFormat("domain"));
+
+ /*
+ * colour by label (no threshold)
+ */
+ fc = new FeatureColour();
+ fc.setColourByLabel(true);
+ assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
+
+ /*
+ * colour by label (autoscaled) (an odd state you can reach by selecting
+ * 'above threshold', then deselecting 'threshold is min/max' then 'colour
+ * by label')
+ */
+ fc.setAutoScaled(true);
+ assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
+
+ /*
+ * colour by label (above threshold) (min/max values are output though not
+ * used by this scheme)
+ */
+ fc.setAutoScaled(false);
+ fc.setThreshold(12.5f);
+ fc.setAboveThreshold(true);
+ assertEquals("domain\tlabel|||0.0|0.0|above|12.5",
+ fc.toJalviewFormat("domain"));
+
+ /*
+ * colour by label (below threshold)
+ */
+ fc.setBelowThreshold(true);
+ assertEquals("domain\tlabel|||0.0|0.0|below|12.5",
+ fc.toJalviewFormat("domain"));
+
+ /*
+ * graduated colour, no threshold
+ */
+ fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
+ String greenHex = Format.getHexString(Color.GREEN);
+ String expected = String.format("domain\t%s|%s|12.0|25.0|none",
+ greenHex, redHex);
+ assertEquals(expected, fc.toJalviewFormat("domain"));
+
+ /*
+ * colour ranges over the actual score ranges (not min/max)
+ */
+ fc.setAutoScaled(true);
+ expected = String.format("domain\t%s|%s|abso|12.0|25.0|none", greenHex,
+ redHex);
+ assertEquals(expected, fc.toJalviewFormat("domain"));
+
+ /*
+ * graduated colour below threshold
+ */
+ fc.setThreshold(12.5f);
+ fc.setBelowThreshold(true);
+ expected = String.format("domain\t%s|%s|abso|12.0|25.0|below|12.5",
+ greenHex, redHex);
+ assertEquals(expected, fc.toJalviewFormat("domain"));
+
+ /*
+ * graduated colour above threshold
+ */
+ fc.setThreshold(12.5f);
+ fc.setAboveThreshold(true);
+ expected = String.format("domain\t%s|%s|abso|12.0|25.0|above|12.5",
+ greenHex, redHex);
+ assertEquals(expected, fc.toJalviewFormat("domain"));
+ }
+
+ /**
+ * Test parsing of feature colours from Jalview features file format
+ */
+ @Test(groups = { "Functional" })
+ public void testParseJalviewFeatureColour()
+ {
+ /*
+ * simple colour by name
+ */
+ FeatureColour fc = FeatureColour.parseJalviewFeatureColour("red");
+ assertTrue(fc.isSimpleColour());
+ assertEquals(Color.RED, fc.getColour());
+
+ /*
+ * simple colour by hex code
+ */
+ fc = FeatureColour.parseJalviewFeatureColour(Format
+ .getHexString(Color.RED));
+ assertTrue(fc.isSimpleColour());
+ assertEquals(Color.RED, fc.getColour());
+
+ /*
+ * simple colour by rgb triplet
+ */
+ fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
+ assertTrue(fc.isSimpleColour());
+ assertEquals(Color.RED, fc.getColour());
+
+ /*
+ * malformed colour
+ */
+ try
+ {
+ fc = FeatureColour.parseJalviewFeatureColour("oops");
+ fail("expected exception");
+ } catch (IllegalArgumentException e)
+ {
+ assertEquals("Invalid colour descriptor: oops", e.getMessage());
+ }
+
+ /*
+ * colour by label (no threshold)
+ */
+ fc = FeatureColour.parseJalviewFeatureColour("label");
+ assertTrue(fc.isColourByLabel());
+ assertFalse(fc.hasThreshold());
+
+ /*
+ * colour by label (with threshold)
+ */
+ fc = FeatureColour
+ .parseJalviewFeatureColour("label|||0.0|0.0|above|12.0");
+ assertTrue(fc.isColourByLabel());
+ assertTrue(fc.isAboveThreshold());
+ assertEquals(12.0f, fc.getThreshold());
+
+ /*
+ * graduated colour (by name) (no threshold)
+ */
+ fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
+ assertTrue(fc.isGraduatedColour());
+ assertFalse(fc.hasThreshold());
+ assertEquals(Color.RED, fc.getMinColour());
+ assertEquals(Color.GREEN, fc.getMaxColour());
+ assertEquals(10f, fc.getMin());
+ assertEquals(20f, fc.getMax());
+ assertTrue(fc.isAutoScaled());
+
+ /*
+ * graduated colour (by hex code) (above threshold)
+ */
+ String descriptor = String.format("%s|%s|10.0|20.0|above|15",
+ Format.getHexString(Color.RED),
+ Format.getHexString(Color.GREEN));
+ fc = FeatureColour.parseJalviewFeatureColour(descriptor);
+ assertTrue(fc.isGraduatedColour());
+ assertTrue(fc.hasThreshold());
+ assertTrue(fc.isAboveThreshold());
+ assertEquals(15f, fc.getThreshold());
+ assertEquals(Color.RED, fc.getMinColour());
+ assertEquals(Color.GREEN, fc.getMaxColour());
+ assertEquals(10f, fc.getMin());
+ assertEquals(20f, fc.getMax());
+ assertTrue(fc.isAutoScaled());
+
+ /*
+ * graduated colour (by RGB triplet) (below threshold), absolute scale
+ */
+ descriptor = String.format("255,0,0|0,255,0|abso|10.0|20.0|below|15");
+ fc = FeatureColour.parseJalviewFeatureColour(descriptor);
+ assertTrue(fc.isGraduatedColour());
+ assertFalse(fc.isAutoScaled());
+ assertTrue(fc.hasThreshold());
+ assertTrue(fc.isBelowThreshold());
+ assertEquals(15f, fc.getThreshold());
+ assertEquals(Color.RED, fc.getMinColour());
+ assertEquals(Color.GREEN, fc.getMaxColour());
+ assertEquals(10f, fc.getMin());
+ assertEquals(20f, fc.getMax());
+ }
}
--- /dev/null
+package jalview.schemes;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertSame;
+
+import java.awt.Color;
+
+import org.testng.annotations.Test;
+public class UserColourSchemeTest
+{
+
+ @Test(groups = "functional")
+ public void testGetColourFromString()
+ {
+ /*
+ * by colour name - if known to AWT, and included in
+ *
+ * @see ColourSchemeProperty.getAWTColorFromName()
+ */
+ assertSame(Color.RED, UserColourScheme.getColourFromString("red"));
+ assertSame(Color.RED, UserColourScheme.getColourFromString("Red"));
+ assertSame(Color.RED, UserColourScheme.getColourFromString(" RED "));
+
+ /*
+ * by RGB hex code
+ */
+ String hexColour = Integer.toHexString(Color.RED.getRGB() & 0xffffff);
+ assertEquals(Color.RED, UserColourScheme.getColourFromString(hexColour));
+ // 'hex' prefixes _not_ wanted here
+ assertNull(UserColourScheme.getColourFromString("0x" + hexColour));
+ assertNull(UserColourScheme.getColourFromString("#" + hexColour));
+
+ /*
+ * 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));
+
+ /*
+ * odds and ends
+ */
+ assertNull(UserColourScheme.getColourFromString(null));
+ assertNull(UserColourScheme.getColourFromString("rubbish"));
+ assertEquals(Color.WHITE, UserColourScheme.getColourFromString("-1"));
+ assertNull(UserColourScheme.getColourFromString(String
+ .valueOf(Integer.MAX_VALUE)));
+ }
+}