From 943e92b0135861f6d400e10f12de8251222a9952 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Mon, 1 Oct 2018 15:06:00 +0100 Subject: [PATCH] JAL-3093 refactor (+test) Annotation Labels tooltip ready for reuse --- src/jalview/gui/AnnotationLabels.java | 124 ++++++++++++++-------------- test/jalview/gui/AnnotationLabelsTest.java | 105 +++++++++++++++++++++++ 2 files changed, 166 insertions(+), 63 deletions(-) create mode 100644 test/jalview/gui/AnnotationLabelsTest.java diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index 6f8b225..2517a9f 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -51,12 +51,9 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; -import java.util.regex.Pattern; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenuItem; @@ -73,6 +70,10 @@ import javax.swing.ToolTipManager; public class AnnotationLabels extends JPanel implements MouseListener, MouseMotionListener, ActionListener { + private static final String HTML_END_TAG = ""; + + private static final String HTML_START_TAG = ""; + /** * width in pixels within which height adjuster arrows are shown and active */ @@ -83,9 +84,6 @@ public class AnnotationLabels extends JPanel */ private static int HEIGHT_ADJUSTER_HEIGHT = 10; - private static final Pattern LEFT_ANGLE_BRACKET_PATTERN = Pattern - .compile("<"); - private static final Font font = new Font("Arial", Font.PLAIN, 11); private static final String TOGGLE_LABELSCALE = MessageManager @@ -708,70 +706,70 @@ public class AnnotationLabels extends JPanel AlignmentAnnotation aa = ap.av.getAlignment() .getAlignmentAnnotation()[selectedRow]; - StringBuffer desc = new StringBuffer(); - if (aa.description != null - && !aa.description.equals("New description")) - { - // TODO: we could refactor and merge this code with the code in - // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature - // tooltips - desc.append(aa.getDescription(true).trim()); - // check to see if the description is an html fragment. - if (desc.length() < 6 || (desc.substring(0, 6).toLowerCase() - .indexOf("") < 0)) - { - // clean the description ready for embedding in html - desc = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(desc) - .replaceAll("<")); - desc.insert(0, ""); - } - else - { - // remove terminating html if any - int i = desc.substring(desc.length() - 7).toLowerCase() - .lastIndexOf(""); - if (i > -1) - { - desc.setLength(desc.length() - 7 + i); - } - } - if (aa.hasScore()) - { - desc.append("
"); - } - // if (aa.hasProperties()) - // { - // desc.append(""); - // for (String prop : aa.getProperties()) - // { - // desc.append(""); - // } - // desc.append("
" + prop + "" - // + aa.getProperty(prop) + "
"); - // } - } - else + String desc = getTooltip(aa); + this.setToolTipText(desc); + } + } + + /** + * Answers a tooltip, formatted as html, containing the annotation description + * (prefixed by associated sequence id if applicable), and the annotation + * (non-positional) score if it has one. Answers null if neither description + * nor score is found. + * + * @param aa + * @return + */ + static String getTooltip(AlignmentAnnotation aa) + { + if (aa == null) + { + return null; + } + StringBuilder tooltip = new StringBuilder(); + if (aa.description != null && !aa.description.equals("New description")) + { + // TODO: we could refactor and merge this code with the code in + // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature + // tooltips + String desc = aa.getDescription(true).trim(); + if (!desc.toLowerCase().startsWith(HTML_START_TAG)) { - // begin the tooltip's html fragment - desc.append(""); - if (aa.hasScore()) - { - // TODO: limit precision of score to avoid noise from imprecise - // doubles - // (64.7 becomes 64.7+/some tiny value). - desc.append(" Score: " + aa.score); - } + tooltip.append(HTML_START_TAG); + desc = desc.replace("<", "<"); } - if (desc.length() > 6) + else if (desc.toLowerCase().endsWith(HTML_END_TAG)) { - desc.append(""); - this.setToolTipText(desc.toString()); + desc = desc.substring(0, desc.length() - HTML_END_TAG.length()); } - else + tooltip.append(desc); + } + else + { + // begin the tooltip's html fragment + tooltip.append(HTML_START_TAG); + } + if (aa.hasScore()) + { + if (tooltip.length() > HTML_START_TAG.length()) { - this.setToolTipText(null); + tooltip.append("
"); } + // TODO: limit precision of score to avoid noise from imprecise + // doubles + // (64.7 becomes 64.7+/some tiny value). + tooltip.append(" Score: ").append(String.valueOf(aa.score)); } + + if (tooltip.length() > HTML_START_TAG.length()) + { + return tooltip.append(HTML_END_TAG).toString(); + } + + /* + * nothing in the tooltip (except "") + */ + return null; } /** diff --git a/test/jalview/gui/AnnotationLabelsTest.java b/test/jalview/gui/AnnotationLabelsTest.java new file mode 100644 index 0000000..31839a9 --- /dev/null +++ b/test/jalview/gui/AnnotationLabelsTest.java @@ -0,0 +1,105 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.Sequence; + +import org.testng.annotations.Test; + +public class AnnotationLabelsTest +{ + @Test(groups = "Functional") + public void testGetTooltip() + { + assertNull(AnnotationLabels.getTooltip(null)); + + /* + * simple description only + */ + AlignmentAnnotation ann = new AlignmentAnnotation("thelabel", "thedesc", + null); + String expected = "thedesc"; + assertEquals(AnnotationLabels.getTooltip(ann), expected); + + /* + * description needing html encoding + * (no idea why '<' is encoded but '>' is not) + */ + ann.description = "TCoffee scores < 56 and > 28"; + expected = "TCoffee scores < 56 and > 28"; + assertEquals(AnnotationLabels.getTooltip(ann), expected); + + /* + * description already html formatted + */ + ann.description = "hello world"; + assertEquals(AnnotationLabels.getTooltip(ann), ann.description); + + /* + * simple description and score + */ + ann.description = "hello world"; + ann.setScore(2.34d); + expected = "hello world
Score: 2.34"; + assertEquals(AnnotationLabels.getTooltip(ann), expected); + + /* + * html description and score + */ + ann.description = "hello world"; + ann.setScore(2.34d); + expected = "hello world
Score: 2.34"; + assertEquals(AnnotationLabels.getTooltip(ann), expected); + + /* + * score, no description + */ + ann.description = " "; + assertEquals(AnnotationLabels.getTooltip(ann), + " Score: 2.34"); + ann.description = null; + assertEquals(AnnotationLabels.getTooltip(ann), + " Score: 2.34"); + + /* + * sequenceref, simple description + */ + ann.description = "Count < 12"; + ann.sequenceRef = new Sequence("Seq1", "MLRIQST"); + ann.hasScore = false; + ann.score = Double.NaN; + expected = "Seq1 : Count < 12"; + assertEquals(AnnotationLabels.getTooltip(ann), expected); + + /* + * sequenceref, html description, score + */ + ann.description = "Score < 4.8"; + ann.sequenceRef = new Sequence("Seq1", "MLRIQST"); + ann.setScore(-2.1D); + expected = "Seq1 : Score < 4.8
Score: -2.1"; + assertEquals(AnnotationLabels.getTooltip(ann), expected); + + /* + * no score, null description + */ + ann.description = null; + ann.hasScore = false; + ann.score = Double.NaN; + assertNull(AnnotationLabels.getTooltip(ann)); + + /* + * no score, empty description, sequenceRef + */ + ann.description = ""; + assertEquals(AnnotationLabels.getTooltip(ann), "Seq1 :"); + + /* + * no score, empty description, no sequenceRef + */ + ann.sequenceRef = null; + assertNull(AnnotationLabels.getTooltip(ann)); + } +} -- 1.7.10.2