mirror of
https://github.com/frosch95/SmartCSV.fx.git
synced 2026-04-11 13:38:23 +02:00
support of RichTextFX to have syntax highlighting in the groovy edit field of validation editor
This commit is contained in:
@@ -30,6 +30,7 @@ dependencies {
|
||||
compile group: 'com.google.code.gson', name: 'gson', version: '2.7'
|
||||
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.6.2'
|
||||
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.6.2'
|
||||
compile group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.6.10'
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
|
||||
@@ -32,18 +32,29 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import ninja.javafx.smartcsv.fx.FXMLController;
|
||||
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
import org.fxmisc.richtext.LineNumberFactory;
|
||||
import org.fxmisc.richtext.StyleSpans;
|
||||
import org.fxmisc.richtext.StyleSpansBuilder;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
/**
|
||||
* controller for editing column validations
|
||||
*
|
||||
* RichText groovy highlighting code is based on the java example of
|
||||
* https://github.com/TomasMikula/RichTextFX
|
||||
*/
|
||||
@Component
|
||||
public class ValidationEditorController extends FXMLController {
|
||||
@@ -51,6 +62,39 @@ public class ValidationEditorController extends FXMLController {
|
||||
private StringProperty selectedColumn = new SimpleStringProperty();
|
||||
private ValidationConfiguration validationConfiguration;
|
||||
|
||||
private static final String[] KEYWORDS = new String[] {
|
||||
"abstract", "assert", "boolean", "break", "byte",
|
||||
"case", "catch", "char", "class",
|
||||
"continue", "def", "default", "do", "double", "else",
|
||||
"enum", "extends", "false", "final", "finally", "float",
|
||||
"for", "if", "implements", "import", "in",
|
||||
"instanceof", "int", "interface", "length", "long", "native",
|
||||
"new", "null", "package", "private", "property", "protected", "public",
|
||||
"return", "short", "static", "super",
|
||||
"switch", "synchronized", "this", "threadsafe", "throw", "throws",
|
||||
"transient", "true", "try", "void", "volatile", "while"
|
||||
};
|
||||
|
||||
private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b";
|
||||
private static final String PAREN_PATTERN = "\\(|\\)";
|
||||
private static final String BRACE_PATTERN = "\\{|\\}";
|
||||
private static final String BRACKET_PATTERN = "\\[|\\]";
|
||||
private static final String SEMICOLON_PATTERN = "\\;";
|
||||
private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\"";
|
||||
private static final String STRING2_PATTERN = "'([^'\\\\]|\\\\.)*'";
|
||||
private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/";
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile(
|
||||
"(?<KEYWORD>" + KEYWORD_PATTERN + ")"
|
||||
+ "|(?<PAREN>" + PAREN_PATTERN + ")"
|
||||
+ "|(?<BRACE>" + BRACE_PATTERN + ")"
|
||||
+ "|(?<BRACKET>" + BRACKET_PATTERN + ")"
|
||||
+ "|(?<SEMICOLON>" + SEMICOLON_PATTERN + ")"
|
||||
+ "|(?<STRING>" + STRING_PATTERN + ")"
|
||||
+ "|(?<STRING2>" + STRING2_PATTERN + ")"
|
||||
+ "|(?<COMMENT>" + COMMENT_PATTERN + ")"
|
||||
);
|
||||
|
||||
@FXML
|
||||
private CheckBox notEmptyRuleCheckBox;
|
||||
|
||||
@@ -79,7 +123,7 @@ public class ValidationEditorController extends FXMLController {
|
||||
private TextField valueOfRuleTextField;
|
||||
|
||||
@FXML
|
||||
private TextArea groovyRuleTextArea;
|
||||
private CodeArea groovyRuleTextArea;
|
||||
|
||||
@FXML
|
||||
private CheckBox enableNotEmptyRule;
|
||||
@@ -133,7 +177,7 @@ public class ValidationEditorController extends FXMLController {
|
||||
initTextInputControl(dateformatRuleTextField, enableDateRule);
|
||||
initTextInputControl(regexpRuleTextField, enableRegexpRule);
|
||||
initTextInputControl(valueOfRuleTextField, enableValueOfRule);
|
||||
initTextInputControl(groovyRuleTextArea, enableGroovyRule);
|
||||
initCodeAreaControl(groovyRuleTextArea, enableGroovyRule);
|
||||
|
||||
selectedColumn.addListener(observable -> {
|
||||
updateForm();
|
||||
@@ -275,7 +319,7 @@ public class ValidationEditorController extends FXMLController {
|
||||
enableValueOfRule
|
||||
);
|
||||
|
||||
updateTextInputControl(
|
||||
updateCodeAreaControl(
|
||||
groovyRuleTextArea,
|
||||
validationConfiguration.getGroovyRuleFor(getSelectedColumn()),
|
||||
enableGroovyRule
|
||||
@@ -318,6 +362,15 @@ public class ValidationEditorController extends FXMLController {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCodeAreaControl(CodeArea rule, String value, CheckBox ruleEnabled) {
|
||||
if (value == null) {
|
||||
ruleEnabled.setSelected(false);
|
||||
} else {
|
||||
ruleEnabled.setSelected(true);
|
||||
rule.replaceText(0, 0, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void initCheckBox(CheckBox rule, CheckBox ruleEnabled) {
|
||||
rule.disableProperty().bind(ruleEnabled.selectedProperty().not());
|
||||
ruleEnabled.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
@@ -345,4 +398,43 @@ public class ValidationEditorController extends FXMLController {
|
||||
});
|
||||
}
|
||||
|
||||
private void initCodeAreaControl(CodeArea rule, CheckBox ruleEnabled) {
|
||||
rule.disableProperty().bind(ruleEnabled.selectedProperty().not());
|
||||
ruleEnabled.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue) {
|
||||
rule.clear();
|
||||
}
|
||||
});
|
||||
rule.setParagraphGraphicFactory(LineNumberFactory.get(rule));
|
||||
rule.richChanges()
|
||||
.filter(ch -> !ch.getInserted().equals(ch.getRemoved())) // XXX
|
||||
.subscribe(change -> {
|
||||
rule.setStyleSpans(0, computeHighlighting(rule.getText()));
|
||||
});
|
||||
}
|
||||
|
||||
private static StyleSpans<Collection<String>> computeHighlighting(String text) {
|
||||
Matcher matcher = PATTERN.matcher(text);
|
||||
int lastKwEnd = 0;
|
||||
StyleSpansBuilder<Collection<String>> spansBuilder
|
||||
= new StyleSpansBuilder<>();
|
||||
while(matcher.find()) {
|
||||
String styleClass =
|
||||
matcher.group("KEYWORD") != null ? "keyword" :
|
||||
matcher.group("PAREN") != null ? "paren" :
|
||||
matcher.group("BRACE") != null ? "brace" :
|
||||
matcher.group("BRACKET") != null ? "bracket" :
|
||||
matcher.group("SEMICOLON") != null ? "semicolon" :
|
||||
matcher.group("STRING") != null ? "string" :
|
||||
matcher.group("STRING2") != null ? "string" :
|
||||
matcher.group("COMMENT") != null ? "comment" :
|
||||
null; /* never happens */ assert styleClass != null;
|
||||
spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
|
||||
spansBuilder.add(Collections.singleton(styleClass), matcher.end() - matcher.start());
|
||||
lastKwEnd = matcher.end();
|
||||
}
|
||||
spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
|
||||
return spansBuilder.create();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="junit" />
|
||||
@@ -80,7 +81,8 @@
|
||||
<Hyperlink onAction="#openLinkInBrowser" text="http://fxexperience.com/controlsfx/" GridPane.columnIndex="1" GridPane.rowIndex="8" />
|
||||
<Label text="Apache Log4j 2" GridPane.rowIndex="9" />
|
||||
<Hyperlink onAction="#openLinkInBrowser" text="http://logging.apache.org/log4j/2.x/" GridPane.columnIndex="1" GridPane.rowIndex="9" />
|
||||
|
||||
<Label text="RichTextFX" GridPane.rowIndex="10" />
|
||||
<Hyperlink onAction="#openLinkInBrowser" text="https://github.com/TomasMikula/RichTextFX" GridPane.columnIndex="1" GridPane.rowIndex="10" />
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="4.0" left="8.0" right="8.0" top="4.0" />
|
||||
|
||||
@@ -180,3 +180,34 @@
|
||||
-fx-font-size: 12px;
|
||||
-fx-padding: 10 10 10 10;
|
||||
}
|
||||
|
||||
.keyword {
|
||||
-fx-fill: purple;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
.semicolon {
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
.paren {
|
||||
-fx-fill: firebrick;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
.bracket {
|
||||
-fx-fill: darkgreen;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
.brace {
|
||||
-fx-fill: teal;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
.string {
|
||||
-fx-fill: blue;
|
||||
}
|
||||
|
||||
.comment {
|
||||
-fx-fill: cadetblue;
|
||||
}
|
||||
|
||||
.paragraph-box:has-caret {
|
||||
-fx-background-color: #f2f9fc;
|
||||
}
|
||||
@@ -10,6 +10,8 @@
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
|
||||
<?import org.fxmisc.richtext.CodeArea?>
|
||||
<?import java.net.URL?>
|
||||
<GridPane hgap="10.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="600.0" vgap="10.0" xmlns="http://javafx.com/javafx/8.0.101" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="CENTER" hgrow="NEVER" maxWidth="50.0" prefWidth="50.0" />
|
||||
@@ -50,7 +52,7 @@
|
||||
<TextField fx:id="dateformatRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="7" />
|
||||
<TextField fx:id="regexpRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="8" />
|
||||
<TextField fx:id="valueOfRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="9" />
|
||||
<TextArea fx:id="groovyRuleTextArea" prefHeight="300.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="10" GridPane.rowSpan="2" />
|
||||
<CodeArea fx:id="groovyRuleTextArea" prefHeight="300.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="10" GridPane.rowSpan="2" />
|
||||
<CheckBox fx:id="enableNotEmptyRule" mnemonicParsing="false" GridPane.rowIndex="1" />
|
||||
<CheckBox fx:id="enableIntegerRule" mnemonicParsing="false" GridPane.rowIndex="2" />
|
||||
<CheckBox fx:id="enableDoubleRule" mnemonicParsing="false" GridPane.rowIndex="3" />
|
||||
@@ -68,4 +70,7 @@
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<stylesheets>
|
||||
<URL value="@/ninja/javafx/smartcsv/fx/smartcsv.css" />
|
||||
</stylesheets>
|
||||
</GridPane>
|
||||
|
||||
Reference in New Issue
Block a user