Coloring Text in JTable Cell

170 views Asked by At

I want to color text in a JTable cell. I'm using a DefaultTableCellRender with HTML Tags to color multible words/text. I'm using Regex to find words/text and replacing them by adding HTML Tags.

The problem here is that the HTML tags them selves should not match by the regex.

example:

Text:

This is a example text background

text to color "a example":

This is <font color="FFFFFF" style="background-color: #FFAABB">a example</font> 
text background

next word to color "back":

This is <font color="FFFFFF" style="background-color: #FFAABB">a example</font> 
text <font color="FFFFFF" style="background-color: #AAAAAA">back</font>ground

the "back" in the HTML tag should not be replaced. Is there a way to exclude this by the Regex?

code:

private String color(String val, ArrayList<ColorKeyWord> list) {
        for(ColorKeyWord ckw: list){
            val = val.replaceAll(ckw.getKeyWord(), "<font color=\"" + DecodedDataHTMLTags.color_white + "\" " +"style=\"background-color: #" + ckw.getColor() + "\">" + ckw.getKeyWord() + "</font>");
        }
        return val;
    }

I think a simpler solution would be to us StyledLabel from jidesoft and use StyleRange. But the StyledTableCellRenderer is not included in the free library. I'm also using JTable because i need variable cell height. This can not be achieved with swt tables.

3

There are 3 answers

0
MadProgrammer On BEST ANSWER

There's probably a better way, but basically, what this does it sets up a series of optional groups which allows a Pattern and Matcher to break the String down into "ranges"

We then use those ranges to "inject" the rules...

String text = "This is a example text background and bunnies are red";
Pattern p = Pattern.compile("(example text)|(back)|(bunnies)|(red)", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(text);

List<MatchRange> ranges = new ArrayList<>(25);
while (m.find()) {
    ranges.add(new MatchRange(m.start(), m.end()));
}

StringBuilder sb = new StringBuilder(64);
sb.append("<html>");
int anchor = 0;
for (MatchRange range : ranges) {
    String before = "";
    if (anchor < range.getStart()) {
        before = text.substring(anchor, range.getStart());
    }
    sb.append(before);
    System.out.println(range.getStart() + " - " + range.getEnd());
    String match = text.substring(range.getStart(), range.getEnd());
    // This is where I would have a rule formatter
    if (match.equals("example text")) {
        sb.append("<font color=\"FFFFFF\" style=\"background-color: #FFAABB\">");
        sb.append(match);
        sb.append("</font>");
    } else if (match.equals("back")) {
        sb.append("<font color=\"FFFFFF\" style=\"background-color: #AAAAAA\">");
        sb.append(match);
        sb.append("</font>");
    } else if (match.equals("bunnies")) {
        sb.append("<font color=\"FF0000\" style=\"background-color: #FFFFFF\">");
        sb.append(match);
        sb.append("</font>");
    } else if (match.equals("red")) {
        sb.append("<font color=\"FF0000\" style=\"background-color: #000000\">");
        sb.append(match);
        sb.append("</font>");
    } else {
        sb.append(match);
    }
    anchor = range.getEnd();
}

System.out.println(sb.toString());

And the MatchRange...

public class MatchRange {
    private final int start;
    private final int end;

    public MatchRange(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public int getEnd() {
        return end;
    }

    public int getStart() {
        return start;
    }

}

And this basically outputs

<html>This is a <font color="FFFFFF" style="background-color: #FFAABB">example text</font> <font color="FFFFFF" style="background-color: #AAAAAA">back</font>ground and <font color="FF0000" style="background-color: #FFFFFF">bunnies</font> are <font color="FF0000" style="background-color: #000000">red</font>

I added some additional conditions for testing.

What I would do, is create a class which could carry a condition ("example text") and which could format the value (wrapping the HTML around it for example) and simply iterate over these to create the expression and apply the format

Maybe something like...

public interface ConditionFormatter {
    public String getCondition();
    public String applyFormatTo(String text);
    public boolean matches(String text);
}

public class DefaultConditionFormatter implements ConditionFormatter {

    private final String condition;
    private final String preFormat;
    private final String postFormat;

    public DefaultConditionFormatter(String condition, String preFormat, String postFormat) {
        this.condition = condition;
        this.preFormat = preFormat;
        this.postFormat = postFormat;
    }

    @Override
    public String getCondition() {
        return condition;
    }

    @Override
    public String applyFormatTo(String text) {
        return new StringBuilder(preFormat).append(text).append(postFormat).toString();
    }

    @Override
    public boolean matches(String text) {
        return condition.equalsIgnoreCase(text);
    }

}

Which contains the "condition" or "rule" and the format to apply. Normally, I might be tempted to separate the "rule" and the "formatter", but I think you can get the basic idea...

Then you could do something like...

List<ConditionFormatter> formatters = new ArrayList<>(25);
formatters.add(new DefaultConditionFormatter("example text", "<font color=\"FFFFFF\" style=\"background-color: #FFAABB\">", "</font>"));
formatters.add(new DefaultConditionFormatter("back", "<font color=\"FFFFFF\" style=\"background-color: #AAAAAA\">", "</font>"));
formatters.add(new DefaultConditionFormatter("bunnies", "<font color=\"FF0000\" style=\"background-color: #FFFFFF\">", "</font>"));
formatters.add(new DefaultConditionFormatter("red", "<font color=\"FF0000\" style=\"background-color: #000000\">", "</font>"));

String text = "This is a example text background and bunnies are red";
StringJoiner sj = new StringJoiner(")|(", "(", ")");
for (ConditionFormatter formatter : formatters) {
    sj.add(formatter.getCondition());
}

Pattern p = Pattern.compile(sj.toString(), Pattern.CASE_INSENSITIVE);
//...

And...

for (MatchRange range : ranges) {
    //...
    // This is where I would have a rule formatter
    String match = text.substring(range.getStart(), range.getEnd());
    for (ConditionFormatter formatter : formatters) {
        if (formatter.matches(match)) {
            sb.append(formatter.applyFormatTo(match));
            break;
        }
    }
    //...
}
0
flaz14 On

You need to use more complex regular expression pattern for replacing (and ignoring keywords inside html tags). The 'quick and dirty' solution:

private static String color(String val, String keyword) {
    String pattern = "[^>].*(" + keyword + ").*[^<]";

    Pattern r = Pattern.compile(pattern);
    Matcher m = r.matcher(val);
    if (m.find()) {
        int startIndex = m.start(1);
        int endIndex = m.end(1);
        String withReplacedKeyword = val.substring(0, startIndex)
                + "<font color=\"FFFFFF\" style=\"background-color: #FFAABB\">"
                + keyword + "</font>" + val.substring(endIndex);
        return withReplacedKeyword;
    }       
    return val;

}

public static void main(String[] args) {
    System.out.println(color("This is a example text background", "back"));
    System.out.println(color("This is a example text <font color=\"FFFFFF\" style=\"background-color: #FFAABB\">back</font>ground", "back"));
    System.out.println(color("This is a example text <font color=\"FFFFFF\" style=\"background-color: #FFAABB\">back</font>ground", "is")); 
}

Thus the 1st replacement:

This is a example text 
<font color="FFFFFF" style="background-color: #FFAABB">
    back
</font>
ground

And the 2nd looks like nested tags (will not broke GUI components):

This is a example text 
<font color="FFFFFF" style="background-color: #FFAABB">
    <font color="FFFFFF" style="background-color: #FFAABB">
        back
    </font>
</font>
ground

The only drawback is that some corner cases (like replacing at the beginning of the original string) can require additional logic. And composing universal regex pattern for all these cases can be laborious.

1
Mr Robbes On

Try setColor(BLACK); on the onject.