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: '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-api', version: '2.6.2'
|
||||||
compile group: 'org.apache.logging.log4j', name: 'log4j-core', 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) {
|
task wrapper(type: Wrapper) {
|
||||||
|
|||||||
@@ -32,18 +32,29 @@ import javafx.fxml.FXML;
|
|||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import ninja.javafx.smartcsv.fx.FXMLController;
|
import ninja.javafx.smartcsv.fx.FXMLController;
|
||||||
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
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.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* controller for editing column validations
|
* controller for editing column validations
|
||||||
|
*
|
||||||
|
* RichText groovy highlighting code is based on the java example of
|
||||||
|
* https://github.com/TomasMikula/RichTextFX
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ValidationEditorController extends FXMLController {
|
public class ValidationEditorController extends FXMLController {
|
||||||
@@ -51,6 +62,39 @@ public class ValidationEditorController extends FXMLController {
|
|||||||
private StringProperty selectedColumn = new SimpleStringProperty();
|
private StringProperty selectedColumn = new SimpleStringProperty();
|
||||||
private ValidationConfiguration validationConfiguration;
|
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
|
@FXML
|
||||||
private CheckBox notEmptyRuleCheckBox;
|
private CheckBox notEmptyRuleCheckBox;
|
||||||
|
|
||||||
@@ -79,7 +123,7 @@ public class ValidationEditorController extends FXMLController {
|
|||||||
private TextField valueOfRuleTextField;
|
private TextField valueOfRuleTextField;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextArea groovyRuleTextArea;
|
private CodeArea groovyRuleTextArea;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private CheckBox enableNotEmptyRule;
|
private CheckBox enableNotEmptyRule;
|
||||||
@@ -133,7 +177,7 @@ public class ValidationEditorController extends FXMLController {
|
|||||||
initTextInputControl(dateformatRuleTextField, enableDateRule);
|
initTextInputControl(dateformatRuleTextField, enableDateRule);
|
||||||
initTextInputControl(regexpRuleTextField, enableRegexpRule);
|
initTextInputControl(regexpRuleTextField, enableRegexpRule);
|
||||||
initTextInputControl(valueOfRuleTextField, enableValueOfRule);
|
initTextInputControl(valueOfRuleTextField, enableValueOfRule);
|
||||||
initTextInputControl(groovyRuleTextArea, enableGroovyRule);
|
initCodeAreaControl(groovyRuleTextArea, enableGroovyRule);
|
||||||
|
|
||||||
selectedColumn.addListener(observable -> {
|
selectedColumn.addListener(observable -> {
|
||||||
updateForm();
|
updateForm();
|
||||||
@@ -275,7 +319,7 @@ public class ValidationEditorController extends FXMLController {
|
|||||||
enableValueOfRule
|
enableValueOfRule
|
||||||
);
|
);
|
||||||
|
|
||||||
updateTextInputControl(
|
updateCodeAreaControl(
|
||||||
groovyRuleTextArea,
|
groovyRuleTextArea,
|
||||||
validationConfiguration.getGroovyRuleFor(getSelectedColumn()),
|
validationConfiguration.getGroovyRuleFor(getSelectedColumn()),
|
||||||
enableGroovyRule
|
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) {
|
private void initCheckBox(CheckBox rule, CheckBox ruleEnabled) {
|
||||||
rule.disableProperty().bind(ruleEnabled.selectedProperty().not());
|
rule.disableProperty().bind(ruleEnabled.selectedProperty().not());
|
||||||
ruleEnabled.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
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 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>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Label text="junit" />
|
<Label text="junit" />
|
||||||
@@ -80,7 +81,8 @@
|
|||||||
<Hyperlink onAction="#openLinkInBrowser" text="http://fxexperience.com/controlsfx/" GridPane.columnIndex="1" GridPane.rowIndex="8" />
|
<Hyperlink onAction="#openLinkInBrowser" text="http://fxexperience.com/controlsfx/" GridPane.columnIndex="1" GridPane.rowIndex="8" />
|
||||||
<Label text="Apache Log4j 2" GridPane.rowIndex="9" />
|
<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" />
|
<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>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="4.0" left="8.0" right="8.0" top="4.0" />
|
<Insets bottom="4.0" left="8.0" right="8.0" top="4.0" />
|
||||||
|
|||||||
@@ -179,4 +179,35 @@
|
|||||||
-fx-text-fill: black;
|
-fx-text-fill: black;
|
||||||
-fx-font-size: 12px;
|
-fx-font-size: 12px;
|
||||||
-fx-padding: 10 10 10 10;
|
-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.GridPane?>
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?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">
|
<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>
|
||||||
<ColumnConstraints halignment="CENTER" hgrow="NEVER" maxWidth="50.0" prefWidth="50.0" />
|
<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="dateformatRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="7" />
|
||||||
<TextField fx:id="regexpRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="8" />
|
<TextField fx:id="regexpRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="8" />
|
||||||
<TextField fx:id="valueOfRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="9" />
|
<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="enableNotEmptyRule" mnemonicParsing="false" GridPane.rowIndex="1" />
|
||||||
<CheckBox fx:id="enableIntegerRule" mnemonicParsing="false" GridPane.rowIndex="2" />
|
<CheckBox fx:id="enableIntegerRule" mnemonicParsing="false" GridPane.rowIndex="2" />
|
||||||
<CheckBox fx:id="enableDoubleRule" mnemonicParsing="false" GridPane.rowIndex="3" />
|
<CheckBox fx:id="enableDoubleRule" mnemonicParsing="false" GridPane.rowIndex="3" />
|
||||||
@@ -68,4 +70,7 @@
|
|||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
</padding>
|
</padding>
|
||||||
|
<stylesheets>
|
||||||
|
<URL value="@/ninja/javafx/smartcsv/fx/smartcsv.css" />
|
||||||
|
</stylesheets>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|||||||
Reference in New Issue
Block a user