11package root .javafx .CustomView ;
22
33import java .io .IOException ;
4-
4+ import java .util .Collection ;
5+ import java .util .Collections ;
6+ import java .util .function .Consumer ;
7+ import java .util .function .Function ;
8+ import java .util .regex .Matcher ;
9+ import java .util .regex .Pattern ;
10+
11+ import org .fxmisc .richtext .GenericStyledArea ;
512import org .fxmisc .richtext .StyleClassedTextArea ;
13+ import org .fxmisc .richtext .model .Paragraph ;
14+ import org .fxmisc .richtext .model .StyleSpans ;
15+ import org .fxmisc .richtext .model .StyleSpansBuilder ;
16+ import org .reactfx .collection .ListModification ;
617
718import de .jensd .fx .glyphs .fontawesome .FontAwesomeIconView ;
19+ import javafx .application .Platform ;
820import javafx .fxml .FXML ;
921import javafx .fxml .FXMLLoader ;
1022import javafx .geometry .Insets ;
2436public class AlertLogListViewCell extends ListCell <Log > {
2537 private FXMLLoader loader ;
2638
39+ private Pattern KEYWORD_PATTERN ;
40+
2741 @ FXML
2842 AnchorPane rootAP ;
2943 @ FXML
@@ -36,6 +50,11 @@ public class AlertLogListViewCell extends ListCell<Log> {
3650 HBox logContentHBox ;
3751
3852 public AlertLogListViewCell (String ... highlightKeywords ) {
53+ String join = String .join ("|" , highlightKeywords );
54+ KEYWORD_PATTERN = Pattern .compile ("(?<KEYWORD1>\\ b(" + join + ")\\ b)"
55+ + "|(?<KEYWORD2>\\ b(" + join + ")\\ B)"
56+ + "|(?<KEYWORD3>\\ B(" + join + ")\\ b)"
57+ + "|(?<KEYWORD4>\\ B(" + join + ")\\ B)" );
3958 }
4059
4160 @ Override
@@ -80,6 +99,8 @@ protected void updateItem(Log logObj, boolean empty) {
8099 codeArea .getStylesheets ()
81100 .add (getClass ().getResource (baseDir + "/css/alertLogListViewCell.css" ).toExternalForm ());
82101 codeArea .getStyleClass ().add ("code-area" );
102+ codeArea .getVisibleParagraphs ()
103+ .addModificationObserver (new VisibleParagraphStyler <>(codeArea , this ::computeHighlighting ));
83104
84105 codeArea .replaceText (0 , 0 , logObj .getFullLogString ());
85106 if (isErrorLog ) {
@@ -109,4 +130,51 @@ private boolean isErrorLog(String logContent) {
109130 // TODO Remove hard-coding that identifying error log
110131 return logContent .contains ("ORA-" );
111132 }
133+
134+ private StyleSpans <Collection <String >> computeHighlighting (String text ) {
135+ Matcher matcher = KEYWORD_PATTERN .matcher (text );
136+ int lastKeywordEnd = 0 ;
137+ StyleSpansBuilder <Collection <String >> spansBuilder = new StyleSpansBuilder <>();
138+ while (matcher .find ()) {
139+ String styleClass = matcher .group ("KEYWORD1" ) != null ? "keyword"
140+ : matcher .group ("KEYWORD2" ) != null ? "keyword"
141+ : matcher .group ("KEYWORD3" ) != null ? "keyword"
142+ : matcher .group ("KEYWORD4" ) != null ? "keyword" : null ;
143+ spansBuilder .add (Collections .emptyList (), matcher .start () - lastKeywordEnd );
144+ spansBuilder .add (Collections .singleton (styleClass ), matcher .end () - matcher .start ());
145+ lastKeywordEnd = matcher .end ();
146+ }
147+ spansBuilder .add (Collections .emptyList (), text .length () - lastKeywordEnd );
148+
149+ return spansBuilder .create ();
150+ }
151+
152+ private class VisibleParagraphStyler <PS , SEG , S >
153+ implements Consumer <ListModification <? extends Paragraph <PS , SEG , S >>> {
154+ private final GenericStyledArea <PS , SEG , S > area ;
155+ private final Function <String , StyleSpans <S >> computeStyles ;
156+ private int prevParagraph , prevTextLength ;
157+
158+ public VisibleParagraphStyler (GenericStyledArea <PS , SEG , S > area ,
159+ Function <String , StyleSpans <S >> computeStyles ) {
160+ this .computeStyles = computeStyles ;
161+ this .area = area ;
162+ }
163+
164+ @ Override
165+ public void accept (ListModification <? extends Paragraph <PS , SEG , S >> lm ) {
166+ if (lm .getAddedSize () > 0 ) {
167+ int paragraph = Math .min (area .firstVisibleParToAllParIndex () + lm .getFrom (),
168+ area .getParagraphs ().size () - 1 );
169+ String text = area .getText (paragraph , 0 , paragraph , area .getParagraphLength (paragraph ));
170+
171+ if (paragraph != prevParagraph || text .length () != prevTextLength ) {
172+ int startPos = area .getAbsolutePosition (paragraph , 0 );
173+ Platform .runLater (() -> area .setStyleSpans (startPos , computeStyles .apply (text )));
174+ prevTextLength = text .length ();
175+ prevParagraph = paragraph ;
176+ }
177+ }
178+ }
179+ }
112180}
0 commit comments