Search code examples
javaregexjtextpane

Coloring regex word patterns in JTextPane cannot color trailing delimiter


I am almost there thanks to the good people on this list. I just need to please have help with the last bit.

I have a JTextPane where I match certain word patterns and change their color to red. Any word or phrase that starts with pipe/open square brace, and ends with close square brace/pipe, like |[this]|, is matched with the following regex "(\\|\\[[^\\]]*\\])".

This works 99%, but it does not color the trailing | red. And if I try to include this in the regex, by adding a \\| to the end, it fails to match the anything.

Here's my code.

In the button that calls the class, I send a silly string for now that has the delimiters:

private void jButton13ActionPerformed(java.awt.event.ActionEvent evt) {                                          

    JFrame Escapedframe = new JFrame("Skipppy Cars word changer");
    Escapedframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    JTextPane SkippytextPane = new JTextPane();
    SkippytextPane.setDocument(new ElementHighlight());
    SkippytextPane.setText("You should try the |[new model]| car, the |[Skippy 2000]|. The Skippy 2000 comes standard with all the |[features]| of the Bippy 3000, at half the price. You can buy one today at your |[athorized Skippy/Bippy]| dealer.");
    Escapedframe.add(SkippytextPane);

    Escapedframe.setSize(500,400);
    Escapedframe.setLocationRelativeTo(null);
    Escapedframe.setVisible(true);

}  

And then set:

private int pipeCharEnd (String Xtext, int index) {
    while (--index >= 0) {
        if (String.valueOf(Xtext.charAt(index)).matches("\\|")) {
            break;
        }
    }
    return index;
}

private int pipeCharStart (String Xtext, int index) {
    while (index < Xtext.length()) {
        if (String.valueOf(Xtext.charAt(index)).matches("\\|")) {
            break;
        }
        index++;
    }
    return index;
}

And finally the class:

class ElementHighlight extends DefaultStyledDocument {
  private final  MutableAttributeSet XRED = new SimpleAttributeSet();
  final StyleContext myProtected = StyleContext.getDefaultStyleContext();
  final AttributeSet attR = myProtected.addAttribute(myProtected.getEmptySet(), StyleConstants.Foreground, Color.RED);
  final AttributeSet attB = myProtected.addAttribute(myProtected.getEmptySet(), StyleConstants.Foreground, Color.BLUE);

@Override
  public void insertString (int offset, String Pstr, AttributeSet RedBlue) throws BadLocationException
  {
   super.insertString(offset, Pstr, RedBlue);
   String text = getText(0, getLength());
   int pre = pipeCharEnd(text, offset);
   if (pre < 0) pre = 0;
   int post = pipeCharStart(text, offset + Pstr.length());
   int prewords = pre;
   int postwords = pre;

   while (postwords <= post) {
                if (postwords == post || String.valueOf(text.charAt(postwords)).matches("\\|")) {
                    if (text.substring(prewords, postwords).matches("(\\|\\[[^\\]]*\\])"))
                        setCharacterAttributes(prewords, postwords - prewords, attR, false);
                    else
                        setCharacterAttributes(prewords, postwords - prewords, attB, false);
                    prewords = postwords;
                }
                postwords++;
            }


  }


 }

Every delimited phrase is turning red, just as expected, except for the trailing pipe in each case, which remains blue.

As I said, I've gotten this far with the help of the good people on this list. If somebody could teach me how to colorize the trailing pipe in the delimiter, I would be most grateful.


Solution

  • Since you enter the following if condition:

    text.substring(prewords, postwords).matches("(\\|\\[[^\\]]*\\])")
    

    ... it means that the sub String between the indices prewords and postword -1 (included) matches (\\|\\[[^\\]]*\\]). This regex looks for:

    • a | followed by
    • a [ followed by
    • a sequence of any characters except ] followed by
    • a ].

    So, considering this sample String:

    0123|[6789012345678]|12345
    

    The regex would match |[6789012345678] (and not the trailing |). Thus, we would enter the if condition with prewords valued at 4 and postwords set to 20.

    prewords = 4, postwords = 20
        v               v
    0123|[6789012345678]|12345
    

    If you do:

    setCharacterAttributes(prewords, postwords - prewords, attR, false);
    

    ... then you're declaring that:

    • postwords - prewords characters
    • starting from index prewords
    • will be red.

    ... which is 20-4 (16) characters from index 4, which in our sample String represents the following sub String: |[6789012345678] (only 16 characters).

    If you want to also set the colour of one more character... simply add +1 to your method call!

    i.e.:

    //                                                   here
    //                                                     v
    setCharacterAttributes(prewords, postwords - prewords +1, attR, false);