0

Currently, I am implementing a textbox in java swing, which highlights the keywords. I referenced to the 2nd answer given in this question. However, I want only the last occurrence of the word to be highlighted.

For example, when the user is typing "I have swimming at school at 7pm", I want the textbox to highlight only the second "at". I have tried using the negative lookahead in the regex. But it is not working.

I used (at(?!.at)|from|by))) instead of (at|from|by)

What I have tried (Referenced from the link provided):

  if (wordR == after || String.valueOf(text.charAt(wordR)).matches("\\W")) {
                    if (text.substring(wordL, wordR).matches("(\\W)*(at(?!.at)|from|by)"))
                        setCharacterAttributes(wordL, wordR - wordL, attr, false);
                    else
                        setCharacterAttributes(wordL, wordR - wordL, attrBlack, false);
                    wordL = wordR;
                }

I think the regex is not working as the checking is happening while the user is typing, but am not sure on how to solve this problem.

Community
  • 1
  • 1
user3453250
  • 59
  • 1
  • 9
  • 1
    I am not sure using regexp is the simplest nor the fastest way to do this. Wouldn't it be just simpler to parse the text to look for all the words and check if there are duplicates? – Guillaume Polet Apr 12 '15 at 08:58
  • If you're in control of the server then you should certainly change approach - how would this work if you ever want to change the sentence structure or the language... – Wain Apr 12 '15 at 09:00

3 Answers3

1

Why don't you use this method:

public int lastIndexOf(String str)

Returns the index within this string of the last occurrence of the specified substring, searching backward starting at the specified index.

As in:

System.out.println("index =  " + "I have swimming at school at 7pm".lastIndexOf("at"));

You can use the value returned by lastIndexOf() to set the attributes of the String in your Swing component, but obviously, you'll need to reset what you've previously done as explained here: Resetting attributes in a Document after inserting a String

If you don't do this, every occurrence of the word will remain highlighted.

Community
  • 1
  • 1
Bruno Lowagie
  • 71,091
  • 10
  • 98
  • 149
  • Would this work if I am checking while the user is typing on the textbox? – user3453250 Apr 12 '15 at 09:30
  • How would you know if the user intends to write more than one `"at"`. For instance: he starts typing `"I have swimming at school "` and then pauses. Would you already change the `"at"`? Or would you wait until the user continues and adds `"at 7 pm"`? – Bruno Lowagie Apr 12 '15 at 09:40
  • Actually, you are asking a whole different question now. The original question was about detecting the last occurrence of one `String` in another `String`. Now you are asking a question about changing the appearance of that `String` in a text field based on key strokes. Maybe you should post that as a different question, now that you've solved the matter of finding the last occurrence of a `String`. (And yes, you could use `lastIndexOf()` in that context.) – Bruno Lowagie Apr 12 '15 at 09:42
  • No, that was the initial question. That is why I have mentioned that this is happening while the user is typing. Currently, I am checking and changing the colour of the word while the user is typing, however, it is highlighting all the occurrences instead of just the last occurrence. So i am wondering if there is anyway to unhighlight the first occurrence of the word when the second occurrence is detected. – user3453250 Apr 12 '15 at 10:01
  • Sorry for the confusion and inconvenience caused. – user3453250 Apr 12 '15 at 10:14
  • No problems. The comments are there to clarify. – Bruno Lowagie Apr 12 '15 at 10:29
0

Do you have to use regular expressions? indexOf has a starting position as its second parameter from where to start the search. You can just fill it with the position of the first hit. Example script:

String haystack = "I have swimming at school at 7pm";
String needle = " at ";
int pos2 = haystack.indexOf(needle, haystack.indexOf(needle) + 1);
if (pos2 > -1) {
    System.out.println("Found second '" + needle + "' at position " + pos2);
} else {
    System.out.println("Could not find second '" + needle + '"');
}

Outputs: Found second ' at ' at position 25

The procedure becomes a little more complicated if you also want to hit at., at,, at? and at!, but the basic principle stays the same.

aleju
  • 2,286
  • 1
  • 13
  • 8
  • The subject of the question talks about the *last* occurence, hence you don't need the `pos2` parameter, you can just use the `lastIndexOf()` method. – Bruno Lowagie Apr 12 '15 at 09:08
  • Well, in the title he says last, in his text he says `I want only the second occurrence of the word to be highlighted`. If he means second because the last occurrence happens to be the second one in his examples then he should use `lastIndexOf()` instead. – aleju Apr 12 '15 at 09:11
  • I admit that it's confusing. Maybe he says *second* because he knows there is no third. It's hard to tell... – Bruno Lowagie Apr 12 '15 at 09:11
  • Sorry. I was thinking about the example when i typed that. It shld be the last occurrence. Sorry for the confusion. – user3453250 Apr 12 '15 at 09:28
0

Basically you can use the a greedy quantifier before the word you search:

Pattern p = Pattern.compile(".*\\b(at|from|by)\\b");
String s = "I have swimming at school at 7pm";
Matcher m = p.matcher(s);
if (m.lookingAt()) {
    System.out.println(m.group(1));
}

Because .* is greedy, you obtain the last occurrence (or the first from the end of the string).

The main interest of this way is that it continues to work whatever the characters before or after the words you search like punctuation characters because the alternation is surrounded by word boundaries. Word boundaries avoid false positive for example when at is included in an other word.

Note: in a replacement context, you need to capture the start of the string.

Casimir et Hippolyte
  • 83,228
  • 5
  • 85
  • 113