mirror of
https://github.com/frosch95/SmartCSV.fx.git
synced 2026-04-11 21:48:22 +02:00
Compare commits
39 Commits
version_0_
...
0.4
| Author | SHA1 | Date | |
|---|---|---|---|
| 59f38b9abb | |||
| 82c9c336f8 | |||
| f173089b14 | |||
| 4801203378 | |||
| 68a68a51a9 | |||
| e5b19d4ad9 | |||
| 7f8977f399 | |||
| 0214744184 | |||
| 9fe9246c55 | |||
| 9570730478 | |||
| 63b05c2307 | |||
| 328c1b0b88 | |||
| 3aa9303b3e | |||
| c2c29433b7 | |||
| 83638d918f | |||
| 64d5000b62 | |||
| 6e7c3226ab | |||
| 8e7731f554 | |||
| 7dc15760d5 | |||
| d48c6ddeb8 | |||
|
|
dce228123a | ||
|
|
3b220c3fd5 | ||
|
|
028af43510 | ||
|
|
a4a9e9914b | ||
|
|
ecdb4837d4 | ||
|
|
e29bbf4008 | ||
|
|
41a8fb41b1 | ||
|
|
ab6d99815e | ||
|
|
744d86fea4 | ||
|
|
fc26dcc9aa | ||
|
|
10c2592510 | ||
|
|
d9405eb536 | ||
|
|
4c9f468e08 | ||
|
|
c996ce660d | ||
|
|
ef1ad63b3f | ||
|
|
26c06a0908 | ||
|
|
b4cc3f9922 | ||
|
|
16b848adc9 | ||
|
|
4e94cf2091 |
@@ -7,7 +7,14 @@ At work I have the need to fix wrong CSV files from customers. It is hard to fin
|
|||||||
even in a "normal" CSV editor. So I decided to write this simple JavaFX application.
|
even in a "normal" CSV editor. So I decided to write this simple JavaFX application.
|
||||||
|
|
||||||
##Video
|
##Video
|
||||||
[](https://youtu.be/SH0UAtPG6Eg)
|
[](https://youtu.be/eUh_WLx1OwI)
|
||||||
|
|
||||||
|
##Links
|
||||||
|
[GitHub Page](http://frosch95.github.io/SmartCSV.fx/)
|
||||||
|
|
||||||
|
[Wiki & Documentation](https://github.com/frosch95/SmartCSV.fx/wiki)
|
||||||
|
|
||||||
|
binary distribution of the [latest release (0.4)](https://drive.google.com/open?id=0BwY9gBUvn5qmREdCc0FvNDNEQTA)
|
||||||
|
|
||||||
##License
|
##License
|
||||||
###The MIT License (MIT)
|
###The MIT License (MIT)
|
||||||
|
|||||||
23
build.gradle
23
build.gradle
@@ -1,5 +1,5 @@
|
|||||||
group 'ninja.javafx'
|
group 'ninja.javafx'
|
||||||
version '0.3-SNAPSHOT'
|
version '0.4-SNAPSHOT'
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'groovy'
|
apply plugin: 'groovy'
|
||||||
@@ -18,10 +18,21 @@ dependencies {
|
|||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'
|
testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'
|
||||||
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
|
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
|
||||||
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.5'
|
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.7'
|
||||||
compile group: 'org.springframework', name:'spring-context', version: '4.2.4.RELEASE'
|
compile group: 'org.springframework', name:'spring-context', version: '4.3.1.RELEASE'
|
||||||
compile group: 'net.sf.supercsv', name: 'super-csv', version: '2.4.0'
|
compile group: 'net.sf.supercsv', name: 'super-csv', version: '2.4.0'
|
||||||
compile group: 'com.typesafe', name: 'config', version: '1.3.0'
|
compile group: 'commons-validator', name: 'commons-validator', version: '1.5.1'
|
||||||
compile group: 'commons-validator', name: 'commons-validator', version: '1.5.0'
|
compile group: 'de.jensd', name: 'fontawesomefx-commons', version: '8.12'
|
||||||
compile group: 'de.jensd', name: 'fontawesomefx', version: '8.8'
|
compile group: 'de.jensd', name: 'fontawesomefx-fontawesome', version: '4.6.3'
|
||||||
|
compile group: 'de.jensd', name: 'fontawesomefx-materialdesignfont', version: '1.6.50'
|
||||||
|
compile group: 'de.jensd', name: 'fontawesomefx-materialicons', version: '2.2.0'
|
||||||
|
compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.11'
|
||||||
|
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) {
|
||||||
|
gradleVersion = '2.14.1' //version required
|
||||||
}
|
}
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Thu Oct 01 19:02:27 CEST 2015
|
#Sat Jul 23 09:35:51 CEST 2016
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip
|
||||||
|
|||||||
10
gradlew
vendored
10
gradlew
vendored
@@ -42,11 +42,6 @@ case "`uname`" in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
|
||||||
if $cygwin ; then
|
|
||||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
PRG="$0"
|
||||||
@@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
SAVED="`pwd`"
|
||||||
cd "`dirname \"$PRG\"`/" >&-
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
APP_HOME="`pwd -P`"
|
APP_HOME="`pwd -P`"
|
||||||
cd "$SAVED" >&-
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
@@ -114,6 +109,7 @@ fi
|
|||||||
if $cygwin ; then
|
if $cygwin ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
|||||||
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@@ -46,7 +46,7 @@ echo location of your Java installation.
|
|||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
:init
|
||||||
@rem Get command-line arguments, handling Windowz variants
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* read some file
|
* read some file
|
||||||
*/
|
*/
|
||||||
public interface FileReader {
|
public interface FileReader<E> {
|
||||||
|
E getContent();
|
||||||
void read(File filename) throws IOException;
|
void read(File filename) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* write some file
|
* write some file
|
||||||
*/
|
*/
|
||||||
public interface FileWriter {
|
public interface FileWriter<E> {
|
||||||
|
void setContent(E content);
|
||||||
void write(File filename) throws IOException;
|
void write(File filename) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* reads the csv file and stores the values in csv model
|
* reads the csv file and stores the values in csv model
|
||||||
*/
|
*/
|
||||||
@Service
|
public class CSVFileReader extends CSVConfigurable implements FileReader<CSVModel> {
|
||||||
public class CSVFileReader extends CSVConfigurable implements FileReader {
|
|
||||||
|
|
||||||
private CSVModel model;
|
private CSVModel model;
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ public class CSVFileReader extends CSVConfigurable implements FileReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CSVModel getData() {
|
public CSVModel getContent() {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,12 +42,11 @@ import static java.util.stream.Collectors.toMap;
|
|||||||
/**
|
/**
|
||||||
* filewriter for the csv
|
* filewriter for the csv
|
||||||
*/
|
*/
|
||||||
@Service
|
public class CSVFileWriter extends CSVConfigurable implements ninja.javafx.smartcsv.FileWriter<CSVModel> {
|
||||||
public class CSVFileWriter extends CSVConfigurable implements ninja.javafx.smartcsv.FileWriter {
|
|
||||||
|
|
||||||
private CSVModel model;
|
private CSVModel model;
|
||||||
|
|
||||||
public void setModel(CSVModel model) {
|
public void setContent(CSVModel model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
src/main/java/ninja/javafx/smartcsv/files/FileStorage.java
Normal file
78
src/main/java/ninja/javafx/smartcsv/files/FileStorage.java
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package ninja.javafx.smartcsv.files;
|
||||||
|
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import ninja.javafx.smartcsv.FileReader;
|
||||||
|
import ninja.javafx.smartcsv.FileWriter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class stores files and there state
|
||||||
|
* @author abi
|
||||||
|
*/
|
||||||
|
public class FileStorage<E> {
|
||||||
|
|
||||||
|
private FileReader<E> reader;
|
||||||
|
private FileWriter<E> writer;
|
||||||
|
|
||||||
|
public FileStorage(FileReader<E> reader, FileWriter<E> writer) {
|
||||||
|
this.reader = reader;
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BooleanProperty fileChanged = new SimpleBooleanProperty(true);
|
||||||
|
private ObjectProperty<File> file = new SimpleObjectProperty<>();
|
||||||
|
private ObjectProperty<E> content = new SimpleObjectProperty<E>();
|
||||||
|
|
||||||
|
public boolean isFileChanged() {
|
||||||
|
return fileChanged.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty fileChangedProperty() {
|
||||||
|
return fileChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileChanged(boolean fileChanged) {
|
||||||
|
this.fileChanged.set(fileChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return file.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<File> fileProperty() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFile(File file) {
|
||||||
|
this.file.set(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getContent() {
|
||||||
|
return content.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<E> contentProperty() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(E content) {
|
||||||
|
this.content.set(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() throws IOException {
|
||||||
|
reader.read(file.get());
|
||||||
|
setContent(reader.getContent());
|
||||||
|
setFileChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() throws IOException {
|
||||||
|
writer.setContent(content.get());
|
||||||
|
writer.write(file.get());
|
||||||
|
setFileChanged(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,8 +26,9 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.fx;
|
package ninja.javafx.smartcsv.fx;
|
||||||
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.collections.WeakListChangeListener;
|
||||||
|
import javafx.concurrent.WorkerStateEvent;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
@@ -36,12 +37,11 @@ import javafx.scene.control.*;
|
|||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import ninja.javafx.smartcsv.FileReader;
|
|
||||||
import ninja.javafx.smartcsv.FileWriter;
|
|
||||||
import ninja.javafx.smartcsv.csv.CSVFileReader;
|
import ninja.javafx.smartcsv.csv.CSVFileReader;
|
||||||
import ninja.javafx.smartcsv.csv.CSVFileWriter;
|
import ninja.javafx.smartcsv.csv.CSVFileWriter;
|
||||||
|
import ninja.javafx.smartcsv.files.FileStorage;
|
||||||
import ninja.javafx.smartcsv.fx.about.AboutController;
|
import ninja.javafx.smartcsv.fx.about.AboutController;
|
||||||
import ninja.javafx.smartcsv.fx.list.ValidationErrorListCell;
|
import ninja.javafx.smartcsv.fx.list.ErrorSideBar;
|
||||||
import ninja.javafx.smartcsv.fx.preferences.PreferencesController;
|
import ninja.javafx.smartcsv.fx.preferences.PreferencesController;
|
||||||
import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory;
|
import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory;
|
||||||
import ninja.javafx.smartcsv.fx.table.ValidationCellFactory;
|
import ninja.javafx.smartcsv.fx.table.ValidationCellFactory;
|
||||||
@@ -50,10 +50,13 @@ import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
|||||||
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
||||||
import ninja.javafx.smartcsv.fx.util.LoadFileService;
|
import ninja.javafx.smartcsv.fx.util.LoadFileService;
|
||||||
import ninja.javafx.smartcsv.fx.util.SaveFileService;
|
import ninja.javafx.smartcsv.fx.util.SaveFileService;
|
||||||
|
import ninja.javafx.smartcsv.fx.validation.ValidationEditorController;
|
||||||
import ninja.javafx.smartcsv.preferences.PreferencesFileReader;
|
import ninja.javafx.smartcsv.preferences.PreferencesFileReader;
|
||||||
import ninja.javafx.smartcsv.preferences.PreferencesFileWriter;
|
import ninja.javafx.smartcsv.preferences.PreferencesFileWriter;
|
||||||
|
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
||||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||||
import ninja.javafx.smartcsv.validation.ValidationFileReader;
|
import ninja.javafx.smartcsv.validation.ValidationFileReader;
|
||||||
|
import ninja.javafx.smartcsv.validation.ValidationFileWriter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -66,8 +69,10 @@ import java.util.Optional;
|
|||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
|
import static java.text.MessageFormat.format;
|
||||||
import static javafx.application.Platform.exit;
|
import static javafx.application.Platform.exit;
|
||||||
import static javafx.application.Platform.runLater;
|
import static javafx.application.Platform.runLater;
|
||||||
|
import static javafx.beans.binding.Bindings.*;
|
||||||
import static javafx.scene.layout.AnchorPane.*;
|
import static javafx.scene.layout.AnchorPane.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,37 +90,29 @@ public class SmartCSVController extends FXMLController {
|
|||||||
".SmartCSV.fx" +
|
".SmartCSV.fx" +
|
||||||
File.separator + "" +
|
File.separator + "" +
|
||||||
"preferences.json");
|
"preferences.json");
|
||||||
|
public static final String CSV_FILTER_TEXT = "CSV files (*.csv)";
|
||||||
|
public static final String CSV_FILTER_EXTENSION = "*.csv";
|
||||||
|
public static final String JSON_FILTER_TEXT = "JSON files (*.json)";
|
||||||
|
public static final String JSON_FILTER_EXTENSION = "*.json";
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// injections
|
// injections
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PreferencesFileReader preferencesLoader;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PreferencesFileWriter preferencesWriter;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private CSVFileReader csvLoader;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ValidationFileReader validationLoader;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private CSVFileWriter csvFileWriter;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AboutController aboutController;
|
private AboutController aboutController;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PreferencesController preferencesController;
|
private PreferencesController preferencesController;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ValidationEditorController validationEditorController;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LoadFileService loadFileService;
|
private LoadFileService loadFileService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SaveFileService saveFileService;;
|
private SaveFileService saveFileService;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private BorderPane applicationPane;
|
private BorderPane applicationPane;
|
||||||
@@ -129,9 +126,6 @@ public class SmartCSVController extends FXMLController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Label stateName;
|
private Label stateName;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ListView errorList;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private AnchorPane tableWrapper;
|
private AnchorPane tableWrapper;
|
||||||
|
|
||||||
@@ -141,6 +135,47 @@ public class SmartCSVController extends FXMLController {
|
|||||||
@FXML
|
@FXML
|
||||||
private MenuItem saveAsMenuItem;
|
private MenuItem saveAsMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem createConfigMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem loadConfigMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem saveConfigMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem saveAsConfigMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem deleteRowMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private MenuItem addRowMenuItem;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button saveButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button saveAsButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button createConfigButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button loadConfigButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button saveConfigButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button saveAsConfigButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button deleteRowButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button addRowButton;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// members
|
// members
|
||||||
@@ -148,13 +183,16 @@ public class SmartCSVController extends FXMLController {
|
|||||||
|
|
||||||
private ValidationCellFactory cellFactory;
|
private ValidationCellFactory cellFactory;
|
||||||
|
|
||||||
private CSVModel model;
|
|
||||||
private TableView<CSVRow> tableView;
|
private TableView<CSVRow> tableView;
|
||||||
private BooleanProperty fileChanged = new SimpleBooleanProperty(true);
|
private ErrorSideBar errorSideBar;
|
||||||
private ResourceBundle resourceBundle;
|
private ResourceBundle resourceBundle;
|
||||||
private File currentCsvFile;
|
|
||||||
private File currentConfigFile;
|
|
||||||
|
|
||||||
|
private FileStorage<CSVModel> currentCsvFile = new FileStorage<>(new CSVFileReader(), new CSVFileWriter());
|
||||||
|
private FileStorage<ValidationConfiguration> currentConfigFile = new FileStorage<>(new ValidationFileReader(), new ValidationFileWriter());
|
||||||
|
private FileStorage<CsvPreference> csvPreferenceFile = new FileStorage<>(new PreferencesFileReader(), new PreferencesFileWriter());
|
||||||
|
|
||||||
|
private ListChangeListener<ValidationError> errorListListener = c -> tableView.refresh();
|
||||||
|
private WeakListChangeListener<ValidationError> weakErrorListListener = new WeakListChangeListener<>(errorListListener);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// init
|
// init
|
||||||
@@ -163,16 +201,35 @@ public class SmartCSVController extends FXMLController {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resourceBundle) {
|
public void initialize(URL location, ResourceBundle resourceBundle) {
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
saveFileService.setWriter(csvFileWriter);
|
|
||||||
cellFactory = new ValidationCellFactory(resourceBundle);
|
setupTableCellFactory();
|
||||||
errorList.setCellFactory(param -> new ValidationErrorListCell(resourceBundle));
|
setupErrorSideBar(resourceBundle);
|
||||||
errorList.getSelectionModel().selectedItemProperty().addListener(observable -> scrollToError());
|
|
||||||
fileChanged.addListener(observable -> setStateName());
|
bindMenuItemsToContentExistence(currentCsvFile, saveMenuItem, saveAsMenuItem, addRowMenuItem, createConfigMenuItem, loadConfigMenuItem);
|
||||||
setStateName();
|
bindButtonsToContentExistence(currentCsvFile, saveButton, saveAsButton, addRowButton, createConfigButton, loadConfigButton);
|
||||||
loadCsvPreferences();
|
|
||||||
|
bindMenuItemsToContentExistence(currentConfigFile, saveConfigMenuItem, saveAsConfigMenuItem);
|
||||||
|
bindButtonsToContentExistence(currentConfigFile, saveAsConfigButton, saveConfigButton);
|
||||||
|
|
||||||
|
bindCsvFileName();
|
||||||
|
bindConfigFileName();
|
||||||
|
|
||||||
|
csvPreferenceFile.setFile(PREFERENCES_FILE);
|
||||||
|
|
||||||
|
loadCsvPreferencesFromFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupErrorSideBar(ResourceBundle resourceBundle) {
|
||||||
|
errorSideBar = new ErrorSideBar(resourceBundle);
|
||||||
|
errorSideBar.selectedValidationErrorProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
scrollToError(newValue);
|
||||||
|
});
|
||||||
|
applicationPane.setRight(errorSideBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTableCellFactory() {
|
||||||
|
cellFactory = new ValidationCellFactory(resourceBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -192,39 +249,44 @@ public class SmartCSVController extends FXMLController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void openCsv(ActionEvent actionEvent) {
|
public void openCsv(ActionEvent actionEvent) {
|
||||||
currentCsvFile = loadFile(csvLoader,
|
loadFile(CSV_FILTER_TEXT, CSV_FILTER_EXTENSION, "Open CSV", currentCsvFile);
|
||||||
"CSV files (*.csv)",
|
|
||||||
"*.csv",
|
|
||||||
"Open CSV",
|
|
||||||
currentCsvFile);
|
|
||||||
enableSaveMenuItems();
|
|
||||||
setCsvFileName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void openConfig(ActionEvent actionEvent) {
|
public void openConfig(ActionEvent actionEvent) {
|
||||||
currentConfigFile = loadFile(validationLoader,
|
loadFile(JSON_FILTER_TEXT, JSON_FILTER_EXTENSION, "Open Validation Configuration", currentConfigFile);
|
||||||
"JSON files (*.json)",
|
}
|
||||||
"*.json",
|
|
||||||
"Open Validation Configuration",
|
@FXML
|
||||||
currentConfigFile);
|
public void createConfig(ActionEvent actionEvent) {
|
||||||
setConfigFileName();
|
currentConfigFile.setContent(currentCsvFile.getContent().createValidationConfiguration());
|
||||||
|
currentConfigFile.setFile(null);
|
||||||
|
currentConfigFile.setFileChanged(true);
|
||||||
|
resetContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void saveCsv(ActionEvent actionEvent) {
|
public void saveCsv(ActionEvent actionEvent) {
|
||||||
csvFileWriter.setModel(model);
|
useSaveFileService(currentCsvFile);
|
||||||
useSaveFileService(csvFileWriter, currentCsvFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void saveAsCsv(ActionEvent actionEvent) {
|
public void saveAsCsv(ActionEvent actionEvent) {
|
||||||
csvFileWriter.setModel(model);
|
saveFile(CSV_FILTER_TEXT, CSV_FILTER_EXTENSION, currentCsvFile);
|
||||||
currentCsvFile = saveFile(csvFileWriter,
|
}
|
||||||
"CSV files (*.csv)",
|
|
||||||
"*.csv",
|
@FXML
|
||||||
currentCsvFile);
|
public void saveConfig(ActionEvent actionEvent) {
|
||||||
setCsvFileName();
|
if (currentConfigFile.getFile() == null) {
|
||||||
|
saveAsConfig(actionEvent);
|
||||||
|
} else {
|
||||||
|
useSaveFileService(currentConfigFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void saveAsConfig(ActionEvent actionEvent) {
|
||||||
|
saveFile(JSON_FILTER_TEXT, JSON_FILTER_EXTENSION, currentConfigFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -261,13 +323,32 @@ public class SmartCSVController extends FXMLController {
|
|||||||
setCsvPreference(csvPreference);
|
setCsvPreference(csvPreference);
|
||||||
saveCsvPreferences(csvPreference);
|
saveCsvPreferences(csvPreference);
|
||||||
} else {
|
} else {
|
||||||
preferencesController.setCsvPreference(preferencesLoader.getCSVpreference());
|
preferencesController.setCsvPreference(csvPreferenceFile.getContent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void deleteRow(ActionEvent actionEvent) {
|
||||||
|
currentCsvFile.getContent().getRows().removeAll(tableView.getSelectionModel().getSelectedItems());
|
||||||
|
currentCsvFile.setFileChanged(true);
|
||||||
|
resetContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void addRow(ActionEvent actionEvent) {
|
||||||
|
CSVRow row = currentCsvFile.getContent().addRow();
|
||||||
|
for (String column : currentCsvFile.getContent().getHeader()) {
|
||||||
|
row.addValue(column, "");
|
||||||
|
}
|
||||||
|
currentCsvFile.setFileChanged(true);
|
||||||
|
resetContent();
|
||||||
|
|
||||||
|
selectNewRow();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canExit() {
|
public boolean canExit() {
|
||||||
boolean canExit = true;
|
boolean canExit = true;
|
||||||
if (model != null && fileChanged.get()) {
|
if (currentCsvFile.getContent() != null && currentCsvFile.isFileChanged()) {
|
||||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
alert.setTitle(resourceBundle.getString("dialog.exit.title"));
|
alert.setTitle(resourceBundle.getString("dialog.exit.title"));
|
||||||
alert.setHeaderText(resourceBundle.getString("dialog.exit.header.text"));
|
alert.setHeaderText(resourceBundle.getString("dialog.exit.header.text"));
|
||||||
@@ -282,74 +363,107 @@ public class SmartCSVController extends FXMLController {
|
|||||||
return canExit;
|
return canExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showValidationEditor(String column) {
|
||||||
|
validationEditorController.setSelectedColumn(column);
|
||||||
|
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setGraphic(null);
|
||||||
|
alert.setTitle(resourceBundle.getString("dialog.validation.rules.title"));
|
||||||
|
alert.setHeaderText(format(resourceBundle.getString("dialog.validation.rules.header"), column));
|
||||||
|
alert.getDialogPane().setContent(validationEditorController.getView());
|
||||||
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
|
|
||||||
|
if (result.get() == ButtonType.OK){
|
||||||
|
runLater(() -> {
|
||||||
|
validationEditorController.updateConfiguration();
|
||||||
|
currentCsvFile.setFileChanged(true);
|
||||||
|
currentCsvFile.getContent().revalidate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// private methods
|
// private methods
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void enableSaveMenuItems() {
|
private void selectNewRow() {
|
||||||
if (currentCsvFile != null) {
|
int lastRow = tableView.getItems().size()-1;
|
||||||
saveMenuItem.setDisable(false);
|
tableView.scrollTo(lastRow);
|
||||||
saveAsMenuItem.setDisable(false);
|
tableView.requestFocus();
|
||||||
|
tableView.getSelectionModel().select(lastRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindMenuItemsToContentExistence(FileStorage file, MenuItem... items) {
|
||||||
|
for (MenuItem item: items) {
|
||||||
|
item.disableProperty().bind(isNull(file.contentProperty()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCsvFileName() {
|
private void bindButtonsToContentExistence(FileStorage file, Button... items) {
|
||||||
if (currentCsvFile != null) {
|
for (Button item: items) {
|
||||||
csvName.setText(currentCsvFile.getName());
|
item.disableProperty().bind(isNull(file.contentProperty()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindMenuItemsToTableSelection(MenuItem... items) {
|
||||||
|
for (MenuItem item: items) {
|
||||||
|
item.disableProperty().bind(lessThan(tableView.getSelectionModel().selectedIndexProperty(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindButtonsToTableSelection(Button... items) {
|
||||||
|
for (Button item: items) {
|
||||||
|
item.disableProperty().bind(lessThan(tableView.getSelectionModel().selectedIndexProperty(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindCsvFileName() {
|
||||||
|
csvName.textProperty().bind(selectString(currentCsvFile.fileProperty(), "name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindConfigFileName() {
|
||||||
|
configurationName.textProperty().bind(selectString(currentConfigFile.fileProperty(), "name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCsvPreferencesFromFile() {
|
||||||
|
if (csvPreferenceFile.getFile().exists()) {
|
||||||
|
useLoadFileService(csvPreferenceFile, event -> setCsvPreference(csvPreferenceFile.getContent()));
|
||||||
} else {
|
} else {
|
||||||
csvName.setText("");
|
setCsvPreference(CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setConfigFileName() {
|
|
||||||
if (currentConfigFile != null) {
|
|
||||||
configurationName.setText(currentConfigFile.getName());
|
|
||||||
} else {
|
|
||||||
configurationName.setText("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadCsvPreferences() {
|
|
||||||
if (PREFERENCES_FILE.exists()) {
|
|
||||||
useLoadFileService(preferencesLoader, PREFERENCES_FILE);
|
|
||||||
}
|
|
||||||
setCsvPreference(preferencesLoader.getCSVpreference());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveCsvPreferences(CsvPreference csvPreference) {
|
private void saveCsvPreferences(CsvPreference csvPreference) {
|
||||||
try {
|
try {
|
||||||
createPreferenceFile();
|
createPreferenceFile();
|
||||||
preferencesWriter.setCsvPreference(csvPreference);
|
csvPreferenceFile.setContent(csvPreference);
|
||||||
useSaveFileService(preferencesWriter, PREFERENCES_FILE);
|
useSaveFileService(csvPreferenceFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPreferenceFile() throws IOException {
|
private void createPreferenceFile() throws IOException {
|
||||||
if (!PREFERENCES_FILE.exists()) {
|
if (!csvPreferenceFile.getFile().exists()) {
|
||||||
createPreferencesFileFolder();
|
createPreferencesFileFolder();
|
||||||
PREFERENCES_FILE.createNewFile();
|
csvPreferenceFile.getFile().createNewFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPreferencesFileFolder() {
|
private void createPreferencesFileFolder() {
|
||||||
if (!PREFERENCES_FILE.getParentFile().exists()) {
|
if (!csvPreferenceFile.getFile().getParentFile().exists()) {
|
||||||
PREFERENCES_FILE.getParentFile().mkdir();
|
csvPreferenceFile.getFile().getParentFile().mkdir();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCsvPreference(CsvPreference csvPreference) {
|
private void setCsvPreference(CsvPreference csvPreference) {
|
||||||
csvLoader.setCsvPreference(csvPreference);
|
|
||||||
csvFileWriter.setCsvPreference(csvPreference);
|
|
||||||
preferencesController.setCsvPreference(csvPreference);
|
preferencesController.setCsvPreference(csvPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
private File loadFile(FileReader fileReader,
|
private void loadFile(String filterText,
|
||||||
String filterText,
|
|
||||||
String filter,
|
String filter,
|
||||||
String title,
|
String title,
|
||||||
File initChildFile) {
|
FileStorage storageFile) {
|
||||||
final FileChooser fileChooser = new FileChooser();
|
final FileChooser fileChooser = new FileChooser();
|
||||||
|
|
||||||
//Set extension filter
|
//Set extension filter
|
||||||
@@ -357,77 +471,73 @@ public class SmartCSVController extends FXMLController {
|
|||||||
fileChooser.getExtensionFilters().add(extFilter);
|
fileChooser.getExtensionFilters().add(extFilter);
|
||||||
fileChooser.setTitle(title);
|
fileChooser.setTitle(title);
|
||||||
|
|
||||||
if (initChildFile != null) {
|
if (storageFile.getFile() != null) {
|
||||||
fileChooser.setInitialDirectory(initChildFile.getParentFile());
|
fileChooser.setInitialDirectory(storageFile.getFile().getParentFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
//Show open file dialog
|
//Show open file dialog
|
||||||
File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow());
|
File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow());
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
useLoadFileService(fileReader, file);
|
storageFile.setFile(file);
|
||||||
return file;
|
useLoadFileService(storageFile, t -> resetContent());
|
||||||
} else {
|
|
||||||
return initChildFile;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private File saveFile(FileWriter writer, String filterText, String filter, File initFile) {
|
private File saveFile(String filterText, String filter, FileStorage fileStorage) {
|
||||||
File file = initFile;
|
File file = fileStorage.getFile();
|
||||||
if (model != null) {
|
if (fileStorage.getContent() != null) {
|
||||||
final FileChooser fileChooser = new FileChooser();
|
final FileChooser fileChooser = new FileChooser();
|
||||||
|
|
||||||
//Set extension filter
|
//Set extension filter
|
||||||
final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(filterText, filter);
|
final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(filterText, filter);
|
||||||
fileChooser.getExtensionFilters().add(extFilter);
|
fileChooser.getExtensionFilters().add(extFilter);
|
||||||
|
|
||||||
if (initFile != null) {
|
if (fileStorage.getFile() != null) {
|
||||||
fileChooser.setInitialDirectory(initFile.getParentFile());
|
fileChooser.setInitialDirectory(fileStorage.getFile().getParentFile());
|
||||||
fileChooser.setInitialFileName(initFile.getName());
|
fileChooser.setInitialFileName(fileStorage.getFile().getName());
|
||||||
}
|
}
|
||||||
fileChooser.setTitle("Save File");
|
fileChooser.setTitle("Save File");
|
||||||
|
|
||||||
//Show open file dialog
|
//Show open file dialog
|
||||||
file = fileChooser.showSaveDialog(applicationPane.getScene().getWindow());
|
file = fileChooser.showSaveDialog(applicationPane.getScene().getWindow());
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
useSaveFileService(writer, file);
|
fileStorage.setFile(file);
|
||||||
|
useSaveFileService(fileStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void useLoadFileService(FileReader fileReader, File file) {
|
private void useLoadFileService(FileStorage fileStorage, EventHandler<WorkerStateEvent> onSucceededHandler) {
|
||||||
loadFileService.setFile(file);
|
loadFileService.setFileStorage(fileStorage);
|
||||||
loadFileService.setFileReader(fileReader);
|
|
||||||
loadFileService.restart();
|
loadFileService.restart();
|
||||||
loadFileService.setOnSucceeded(event -> runLater(() -> {
|
loadFileService.setOnSucceeded(onSucceededHandler);
|
||||||
resetContent();
|
|
||||||
fileChanged.setValue(false);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void useSaveFileService(FileWriter writer, File file) {
|
private void useSaveFileService(FileStorage fileStorage) {
|
||||||
saveFileService.setFile(file);
|
saveFileService.setFileStorage(fileStorage);
|
||||||
saveFileService.setWriter(writer);
|
|
||||||
saveFileService.restart();
|
saveFileService.restart();
|
||||||
saveFileService.setOnSucceeded(event -> runLater(() -> {
|
saveFileService.setOnSucceeded(t -> resetContent());
|
||||||
resetContent();
|
|
||||||
fileChanged.setValue(false);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new table view and add the new content
|
* Creates new table view and add the new content
|
||||||
*/
|
*/
|
||||||
private void resetContent() {
|
private void resetContent() {
|
||||||
model = csvLoader.getData();
|
if (currentCsvFile.getContent() != null) {
|
||||||
if (model != null) {
|
currentCsvFile.getContent().getValidationError().addListener(weakErrorListListener);
|
||||||
model.setValidator(validationLoader.getValidator());
|
currentCsvFile.getContent().setValidationConfiguration(currentConfigFile.getContent());
|
||||||
|
validationEditorController.setValidationConfiguration(currentConfigFile.getContent());
|
||||||
tableView = new TableView<>();
|
tableView = new TableView<>();
|
||||||
|
|
||||||
for (String column : model.getHeader()) {
|
bindMenuItemsToTableSelection(deleteRowMenuItem);
|
||||||
|
bindButtonsToTableSelection(deleteRowButton);
|
||||||
|
|
||||||
|
for (String column : currentCsvFile.getContent().getHeader()) {
|
||||||
addColumn(column, tableView);
|
addColumn(column, tableView);
|
||||||
}
|
}
|
||||||
tableView.getItems().setAll(model.getRows());
|
|
||||||
|
tableView.getItems().setAll(currentCsvFile.getContent().getRows());
|
||||||
tableView.setEditable(true);
|
tableView.setEditable(true);
|
||||||
|
|
||||||
setBottomAnchor(tableView, 0.0);
|
setBottomAnchor(tableView, 0.0);
|
||||||
@@ -435,8 +545,7 @@ public class SmartCSVController extends FXMLController {
|
|||||||
setLeftAnchor(tableView, 0.0);
|
setLeftAnchor(tableView, 0.0);
|
||||||
setRightAnchor(tableView, 0.0);
|
setRightAnchor(tableView, 0.0);
|
||||||
tableWrapper.getChildren().setAll(tableView);
|
tableWrapper.getChildren().setAll(tableView);
|
||||||
|
errorSideBar.setModel(currentCsvFile.getContent());
|
||||||
errorList.setItems(model.getValidationError());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,14 +559,19 @@ public class SmartCSVController extends FXMLController {
|
|||||||
column.setCellValueFactory(new ObservableMapValueFactory(header));
|
column.setCellValueFactory(new ObservableMapValueFactory(header));
|
||||||
column.setCellFactory(cellFactory);
|
column.setCellFactory(cellFactory);
|
||||||
column.setEditable(true);
|
column.setEditable(true);
|
||||||
|
column.setSortable(false);
|
||||||
|
|
||||||
|
ContextMenu contextMenu = contextMenuForColumn(header);
|
||||||
|
column.setContextMenu(contextMenu);
|
||||||
|
|
||||||
column.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<CSVRow, CSVValue>>() {
|
column.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<CSVRow, CSVValue>>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(TableColumn.CellEditEvent<CSVRow, CSVValue> event) {
|
public void handle(TableColumn.CellEditEvent<CSVRow, CSVValue> event) {
|
||||||
event.getTableView().getItems().get(event.getTablePosition().getRow()).
|
event.getTableView().getItems().get(event.getTablePosition().getRow()).
|
||||||
getColumns().get(header).setValue(event.getNewValue());
|
getColumns().get(header).setValue(event.getNewValue());
|
||||||
runLater(() -> {
|
runLater(() -> {
|
||||||
fileChanged.setValue(true);
|
currentCsvFile.setFileChanged(true);
|
||||||
model.revalidate();
|
currentCsvFile.getContent().revalidate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -465,8 +579,16 @@ public class SmartCSVController extends FXMLController {
|
|||||||
tableView.getColumns().add(column);
|
tableView.getColumns().add(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scrollToError() {
|
private ContextMenu contextMenuForColumn(String header) {
|
||||||
ValidationError entry = (ValidationError)errorList.getSelectionModel().getSelectedItem();
|
ContextMenu contextMenu = new ContextMenu();
|
||||||
|
MenuItem editColumnRulesMenuItem = new MenuItem(resourceBundle.getString("context.menu.edit.column.rules"));
|
||||||
|
bindMenuItemsToContentExistence(currentConfigFile, editColumnRulesMenuItem);
|
||||||
|
editColumnRulesMenuItem.setOnAction(e -> showValidationEditor(header));
|
||||||
|
contextMenu.getItems().addAll(editColumnRulesMenuItem);
|
||||||
|
return contextMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scrollToError(ValidationError entry) {
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
if (entry.getLineNumber() != null) {
|
if (entry.getLineNumber() != null) {
|
||||||
tableView.scrollTo(max(0, entry.getLineNumber() - 1));
|
tableView.scrollTo(max(0, entry.getLineNumber() - 1));
|
||||||
@@ -476,16 +598,4 @@ public class SmartCSVController extends FXMLController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStateName() {
|
|
||||||
if (model != null) {
|
|
||||||
if (fileChanged.get()) {
|
|
||||||
stateName.setText(resourceBundle.getString("state.changed"));
|
|
||||||
} else {
|
|
||||||
stateName.setText(resourceBundle.getString("state.unchanged"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stateName.setText("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
176
src/main/java/ninja/javafx/smartcsv/fx/list/ErrorSideBar.java
Normal file
176
src/main/java/ninja/javafx/smartcsv/fx/list/ErrorSideBar.java
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ninja.javafx.smartcsv.fx.list;
|
||||||
|
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.WeakListChangeListener;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||||
|
import ninja.javafx.smartcsv.fx.util.ColorConstants;
|
||||||
|
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.controlsfx.control.PopOver;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import static javafx.geometry.Pos.CENTER;
|
||||||
|
import static ninja.javafx.smartcsv.fx.util.ColorConstants.ERROR_COLOR;
|
||||||
|
import static ninja.javafx.smartcsv.fx.util.ColorConstants.OK_COLOR;
|
||||||
|
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clickable side bar with error markers
|
||||||
|
*/
|
||||||
|
public class ErrorSideBar extends Region {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(ErrorSideBar.class);
|
||||||
|
|
||||||
|
private static final double WIDTH = 20.0;
|
||||||
|
private static final int BORDER = 8;
|
||||||
|
private static final double STATUS_BLOCK_HEIGHT = WIDTH - BORDER;
|
||||||
|
private static final double STATUS_BLOCK_WIDTH = WIDTH - BORDER;
|
||||||
|
private static final double STATUS_BLOCK_OFFSET = WIDTH + BORDER / 2;
|
||||||
|
|
||||||
|
private ListChangeListener<ValidationError> errorListListener = c -> setErrorMarker();
|
||||||
|
private WeakListChangeListener<ValidationError> weakErrorListListener = new WeakListChangeListener<>(errorListListener);
|
||||||
|
private ObjectProperty<CSVModel> model = new SimpleObjectProperty<>();
|
||||||
|
private ObjectProperty<ValidationError> selectedValidationError = new SimpleObjectProperty<>();
|
||||||
|
private PopOver popOver = new PopOver();
|
||||||
|
private ResourceBundle resourceBundle;
|
||||||
|
private Region statusBlock;
|
||||||
|
|
||||||
|
public ErrorSideBar(ResourceBundle resourceBundle) {
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
initPopOver();
|
||||||
|
setFixWidth();
|
||||||
|
addModelListener();
|
||||||
|
|
||||||
|
statusBlock = new Region();
|
||||||
|
statusBlock.setPrefSize(STATUS_BLOCK_WIDTH, STATUS_BLOCK_HEIGHT);
|
||||||
|
statusBlock.setLayoutY(BORDER / 2);
|
||||||
|
statusBlock.setLayoutX(BORDER / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPopOver() {
|
||||||
|
popOver.setAutoHide(true);
|
||||||
|
popOver.setArrowLocation(PopOver.ArrowLocation.RIGHT_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(CSVModel model) {
|
||||||
|
this.model.set(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSVModel getModel() {
|
||||||
|
return model.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<CSVModel> modelProperty() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationError getSelectedValidationError() {
|
||||||
|
return selectedValidationError.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<ValidationError> selectedValidationErrorProperty() {
|
||||||
|
return selectedValidationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedValidationError(ValidationError selectedValidationError) {
|
||||||
|
this.selectedValidationError.set(selectedValidationError);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addModelListener() {
|
||||||
|
model.addListener((observable, oldValue, newValue) -> {
|
||||||
|
newValue.getValidationError().addListener(weakErrorListListener);
|
||||||
|
setErrorMarker();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFixWidth() {
|
||||||
|
setMinWidth(WIDTH);
|
||||||
|
setPrefWidth(WIDTH);
|
||||||
|
setMaxWidth(WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setErrorMarker() {
|
||||||
|
List<Region> errorMarkerList = new ArrayList<>();
|
||||||
|
errorMarkerList.add(statusBlock);
|
||||||
|
statusBlock.setStyle("-fx-background-color: " + OK_COLOR);
|
||||||
|
if (model.get() != null) {
|
||||||
|
List<ValidationError> errorList = model.get().getValidationError();
|
||||||
|
if (errorList != null && !errorList.isEmpty()) {
|
||||||
|
|
||||||
|
statusBlock.setStyle("-fx-background-color: " + ERROR_COLOR);
|
||||||
|
|
||||||
|
int rows = model.get().getRows().size();
|
||||||
|
double space = (double)heightWithoutStatusBlock() / rows;
|
||||||
|
for (ValidationError error : errorList) {
|
||||||
|
errorMarkerList.add(generateErrorMarker(space, error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getChildren().setAll(errorMarkerList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int heightWithoutStatusBlock() {
|
||||||
|
return (int)(getHeight() - STATUS_BLOCK_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Region generateErrorMarker(double space, ValidationError error) {
|
||||||
|
logger.info("generate error marker for {} errors in line {}", error.getMessages().size(), error.getLineNumber());
|
||||||
|
logger.info("layout y is set to {}", (space * error.getLineNumber() + STATUS_BLOCK_OFFSET));
|
||||||
|
Region errorMarker = new Region();
|
||||||
|
errorMarker.setLayoutY(space * error.getLineNumber() + STATUS_BLOCK_OFFSET);
|
||||||
|
errorMarker.setPrefSize(WIDTH, 2);
|
||||||
|
errorMarker.setStyle("-fx-background-color: " + ERROR_COLOR);
|
||||||
|
errorMarker.setOnMouseClicked(event -> selectedValidationError.setValue(error));
|
||||||
|
errorMarker.setOnMouseEntered(event -> {
|
||||||
|
popOver.setContentNode(popupContent(getI18nValidatioMessage(resourceBundle, error)));
|
||||||
|
popOver.show(errorMarker, -16);
|
||||||
|
});
|
||||||
|
return errorMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node popupContent(String text) {
|
||||||
|
VBox vBox = new VBox();
|
||||||
|
vBox.setPadding(new Insets(10,10,10,10));
|
||||||
|
vBox.getChildren().add(new Text(text));
|
||||||
|
vBox.setAlignment(CENTER);
|
||||||
|
return vBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,12 +26,8 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.fx.preferences;
|
package ninja.javafx.smartcsv.fx.preferences;
|
||||||
|
|
||||||
import javafx.beans.InvalidationListener;
|
|
||||||
import javafx.beans.Observable;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
|
|||||||
@@ -33,10 +33,12 @@ import javafx.scene.control.Tooltip;
|
|||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
||||||
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
||||||
|
import ninja.javafx.smartcsv.fx.util.ColorConstants;
|
||||||
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import static javafx.application.Platform.runLater;
|
import static javafx.application.Platform.runLater;
|
||||||
|
import static ninja.javafx.smartcsv.fx.util.ColorConstants.ERROR_COLOR;
|
||||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +79,7 @@ public class EditableValidationCell extends TableCell<CSVRow, CSVValue> {
|
|||||||
setStyle("");
|
setStyle("");
|
||||||
setTooltip(null);
|
setTooltip(null);
|
||||||
} else if (item.getValidationError() != null) {
|
} else if (item.getValidationError() != null) {
|
||||||
setStyle("-fx-background-color: #ff8888");
|
setStyle("-fx-background-color: derive("+ ERROR_COLOR +", 30%)");
|
||||||
setTooltip(new Tooltip(getI18nValidatioMessage(resourceBundle, item.getValidationError())));
|
setTooltip(new Tooltip(getI18nValidatioMessage(resourceBundle, item.getValidationError())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,16 @@ package ninja.javafx.smartcsv.fx.table.model;
|
|||||||
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.concurrent.Service;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
||||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||||
import ninja.javafx.smartcsv.validation.Validator;
|
import ninja.javafx.smartcsv.validation.Validator;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CSVModel is the client representation for the csv filepath.
|
* The CSVModel is the client representation for the csv filepath.
|
||||||
@@ -37,17 +45,20 @@ import ninja.javafx.smartcsv.validation.Validator;
|
|||||||
*/
|
*/
|
||||||
public class CSVModel {
|
public class CSVModel {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(CSVModel.class);
|
||||||
|
|
||||||
private Validator validator;
|
private Validator validator;
|
||||||
private ObservableList<CSVRow> rows = FXCollections.observableArrayList();
|
private ObservableList<CSVRow> rows = FXCollections.observableArrayList();
|
||||||
private String[] header;
|
private String[] header;
|
||||||
private ObservableList<ValidationError> validationError = FXCollections.observableArrayList();
|
private ObservableList<ValidationError> validationError = FXCollections.observableArrayList();
|
||||||
|
private RevalidationService revalidationService = new RevalidationService();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the validator for the data revalidates
|
* sets the validator configuration for the data revalidates
|
||||||
* @param validator the validator for this data
|
* @param validationConfiguration the validator configuration for this data
|
||||||
*/
|
*/
|
||||||
public void setValidator(Validator validator) {
|
public void setValidationConfiguration(ValidationConfiguration validationConfiguration) {
|
||||||
this.validator = validator;
|
this.validator = new Validator(validationConfiguration);
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +110,62 @@ public class CSVModel {
|
|||||||
public void revalidate() {
|
public void revalidate() {
|
||||||
validationError.clear();
|
validationError.clear();
|
||||||
|
|
||||||
if (header != null && validator != null) {
|
logger.info("revalidate: hasValidator -> {}", hasValidator());
|
||||||
addValidationError(validator.isHeaderValid(header));
|
|
||||||
|
if (!hasValidator()) return;
|
||||||
|
|
||||||
|
validator.clearScriptCache();
|
||||||
|
revalidationService.setHeader(header);
|
||||||
|
revalidationService.setRows(rows);
|
||||||
|
revalidationService.setValidator(validator);
|
||||||
|
revalidationService.setOnSucceeded(t -> validationError.setAll(revalidationService.getValue()));
|
||||||
|
revalidationService.setOnFailed(t -> logger.error("revalidation service failed!"));
|
||||||
|
revalidationService.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasValidator() {
|
||||||
|
return validator != null && validator.hasConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationConfiguration createValidationConfiguration() {
|
||||||
|
ValidationConfiguration newValidationConfiguration = new ValidationConfiguration();
|
||||||
|
newValidationConfiguration.setHeaderNames(this.header);
|
||||||
|
this.validator = new Validator(newValidationConfiguration);
|
||||||
|
this.revalidate();
|
||||||
|
return newValidationConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RevalidationService extends Service<List<ValidationError>> {
|
||||||
|
|
||||||
|
private Validator validator;
|
||||||
|
private List<CSVRow> rows;
|
||||||
|
private String[] header;
|
||||||
|
|
||||||
|
public void setValidator(Validator validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRows(List<CSVRow> rows) {
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeader(String[] header) {
|
||||||
|
this.header = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Task<List<ValidationError>> createTask() {
|
||||||
|
return new Task<List<ValidationError>>() {
|
||||||
|
@Override
|
||||||
|
protected List<ValidationError> call() throws Exception {
|
||||||
|
List<ValidationError> errors = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
if (header != null) {
|
||||||
|
ValidationError headerError = validator.isHeaderValid(header);
|
||||||
|
if (headerError != null) {
|
||||||
|
logger.info("revalidate: header error found");
|
||||||
|
errors.add(headerError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int lineNumber = 0; lineNumber < rows.size(); lineNumber++) {
|
for (int lineNumber = 0; lineNumber < rows.size(); lineNumber++) {
|
||||||
@@ -112,7 +177,8 @@ public class CSVModel {
|
|||||||
if (validator != null) {
|
if (validator != null) {
|
||||||
ValidationError validationError = validator.isValid(column, value.getValue(), lineNumber);
|
ValidationError validationError = validator.isValid(column, value.getValue(), lineNumber);
|
||||||
if (validationError != null) {
|
if (validationError != null) {
|
||||||
addValidationError(validationError);
|
logger.info("revalidate: {} errors found in line {}", validationError.getMessages().size(), lineNumber);
|
||||||
|
errors.add(validationError);
|
||||||
value.setValidationError(validationError);
|
value.setValidationError(validationError);
|
||||||
} else {
|
} else {
|
||||||
value.setValidationError(null);
|
value.setValidationError(null);
|
||||||
@@ -122,11 +188,12 @@ public class CSVModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.error("validation error", t);
|
||||||
}
|
}
|
||||||
|
return errors;
|
||||||
private void addValidationError(ValidationError validationError) {
|
}
|
||||||
if (validationError != null) {
|
};
|
||||||
this.validationError.add(validationError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ public class CSVRow {
|
|||||||
public void addValue(String column, String value) {
|
public void addValue(String column, String value) {
|
||||||
CSVValue v = new CSVValue();
|
CSVValue v = new CSVValue();
|
||||||
v.setValidator(validator);
|
v.setValidator(validator);
|
||||||
v.setValue(value);
|
|
||||||
v.setColumn(column);
|
v.setColumn(column);
|
||||||
v.setRowNumber(rowNumber);
|
v.setRowNumber(rowNumber);
|
||||||
|
v.setValue(value);
|
||||||
columns.put(column, new SimpleObjectProperty<>(v));
|
columns.put(column, new SimpleObjectProperty<>(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ninja.javafx.smartcsv.fx.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of all the important colors
|
||||||
|
*/
|
||||||
|
public class ColorConstants {
|
||||||
|
public static final String OK_COLOR = "#22aa22";
|
||||||
|
public static final String ERROR_COLOR = "#ff6622";
|
||||||
|
}
|
||||||
@@ -40,6 +40,20 @@ import static java.text.MessageFormat.format;
|
|||||||
*/
|
*/
|
||||||
public class I18nValidationUtil {
|
public class I18nValidationUtil {
|
||||||
|
|
||||||
|
public static String getI18nValidatioMessage(ResourceBundle resourceBundle, List<ValidationError> errors) {
|
||||||
|
|
||||||
|
StringWriter message = new StringWriter();
|
||||||
|
for (ValidationError validationError: errors) {
|
||||||
|
message.append(getI18nValidatioMessage(resourceBundle, validationError)).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.toString().length() != 0) {
|
||||||
|
return cutOffLastLineBreak(message.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
public static String getI18nValidatioMessage(ResourceBundle resourceBundle, ValidationError error) {
|
public static String getI18nValidatioMessage(ResourceBundle resourceBundle, ValidationError error) {
|
||||||
|
|
||||||
List<ValidationMessage> validationMessages = error.getMessages();
|
List<ValidationMessage> validationMessages = error.getMessages();
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ package ninja.javafx.smartcsv.fx.util;
|
|||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import ninja.javafx.smartcsv.FileReader;
|
import ninja.javafx.smartcsv.FileReader;
|
||||||
|
import ninja.javafx.smartcsv.files.FileStorage;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -38,14 +39,10 @@ import java.io.File;
|
|||||||
@org.springframework.stereotype.Service
|
@org.springframework.stereotype.Service
|
||||||
public class LoadFileService extends Service {
|
public class LoadFileService extends Service {
|
||||||
|
|
||||||
private File file;
|
private FileStorage file;
|
||||||
private FileReader fileReader;
|
|
||||||
|
|
||||||
public void setFile(File value) {
|
public void setFileStorage(FileStorage file) {
|
||||||
file = value;
|
this.file = file;
|
||||||
}
|
|
||||||
public void setFileReader(FileReader fileReader) {
|
|
||||||
this.fileReader = fileReader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,7 +51,7 @@ public class LoadFileService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
protected Void call() throws Exception {
|
protected Void call() throws Exception {
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
fileReader.read(file);
|
file.load();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import javafx.concurrent.Service;
|
|||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import ninja.javafx.smartcsv.FileWriter;
|
import ninja.javafx.smartcsv.FileWriter;
|
||||||
import ninja.javafx.smartcsv.csv.CSVFileWriter;
|
import ninja.javafx.smartcsv.csv.CSVFileWriter;
|
||||||
|
import ninja.javafx.smartcsv.files.FileStorage;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -41,14 +42,9 @@ import static javafx.application.Platform.runLater;
|
|||||||
@org.springframework.stereotype.Service
|
@org.springframework.stereotype.Service
|
||||||
public class SaveFileService extends Service {
|
public class SaveFileService extends Service {
|
||||||
|
|
||||||
private File file;
|
private FileStorage file;
|
||||||
private FileWriter writer;
|
|
||||||
|
|
||||||
public void setWriter(FileWriter writer) {
|
public void setFileStorage(FileStorage value) {
|
||||||
this.writer = writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFile(File value) {
|
|
||||||
file = value;
|
file = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +54,7 @@ public class SaveFileService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
protected Void call() throws Exception {
|
protected Void call() throws Exception {
|
||||||
try {
|
try {
|
||||||
writer.write(file);
|
file.save();
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,440 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ninja.javafx.smartcsv.fx.validation;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox integerRuleCheckBox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox doublerRuleCheckBox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox alphanumericRuleCheckBox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Spinner<Integer> minLengthSpinner;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Spinner<Integer> maxLengthSpinner;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField dateformatRuleTextField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField regexpRuleTextField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField valueOfRuleTextField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CodeArea groovyRuleTextArea;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableNotEmptyRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableIntegerRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableDoubleRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableAlphanumericRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableMinLengthRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableMaxLengthRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableDateRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableRegexpRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableValueOfRule;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox enableGroovyRule;
|
||||||
|
|
||||||
|
|
||||||
|
@Value("${fxml.smartcvs.validation.editor.view}")
|
||||||
|
@Override
|
||||||
|
public void setFxmlFilePath(String filePath) {
|
||||||
|
this.fxmlFilePath = filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
minLengthSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, 0));
|
||||||
|
maxLengthSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, 0));
|
||||||
|
|
||||||
|
initCheckBox(notEmptyRuleCheckBox, enableNotEmptyRule);
|
||||||
|
initCheckBox(integerRuleCheckBox, enableIntegerRule);
|
||||||
|
initCheckBox(doublerRuleCheckBox, enableDoubleRule);
|
||||||
|
initCheckBox(alphanumericRuleCheckBox, enableAlphanumericRule);
|
||||||
|
initSpinner(minLengthSpinner, enableMinLengthRule);
|
||||||
|
initSpinner(maxLengthSpinner, enableMaxLengthRule);
|
||||||
|
initTextInputControl(dateformatRuleTextField, enableDateRule);
|
||||||
|
initTextInputControl(regexpRuleTextField, enableRegexpRule);
|
||||||
|
initTextInputControl(valueOfRuleTextField, enableValueOfRule);
|
||||||
|
initCodeAreaControl(groovyRuleTextArea, enableGroovyRule);
|
||||||
|
|
||||||
|
selectedColumn.addListener(observable -> {
|
||||||
|
updateForm();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSelectedColumn() {
|
||||||
|
return selectedColumn.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty selectedColumnProperty() {
|
||||||
|
return selectedColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedColumn(String selectedColumn) {
|
||||||
|
this.selectedColumn.set(selectedColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidationConfiguration(ValidationConfiguration validationConfiguration) {
|
||||||
|
this.validationConfiguration = validationConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateConfiguration() {
|
||||||
|
|
||||||
|
if (enableIntegerRule.isSelected()) {
|
||||||
|
validationConfiguration.setIntegerRuleFor(selectedColumn.getValue(), integerRuleCheckBox.isSelected());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setIntegerRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableNotEmptyRule.isSelected()) {
|
||||||
|
validationConfiguration.setNotEmptyRuleFor(selectedColumn.getValue(), notEmptyRuleCheckBox.isSelected());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setNotEmptyRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableDoubleRule.isSelected()) {
|
||||||
|
validationConfiguration.setDoubleRuleFor(selectedColumn.getValue(), doublerRuleCheckBox.isSelected());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setDoubleRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableAlphanumericRule.isSelected()) {
|
||||||
|
validationConfiguration.setAlphanumericRuleFor(selectedColumn.getValue(), alphanumericRuleCheckBox.isSelected());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setAlphanumericRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableDateRule.isSelected()) {
|
||||||
|
validationConfiguration.setDateRuleFor(selectedColumn.getValue(), dateformatRuleTextField.getText());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setDateRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableGroovyRule.isSelected()) {
|
||||||
|
validationConfiguration.setGroovyRuleFor(selectedColumn.getValue(), groovyRuleTextArea.getText());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setGroovyRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableMinLengthRule.isSelected()) {
|
||||||
|
validationConfiguration.setMinLengthRuleFor(selectedColumn.getValue(), minLengthSpinner.getValue());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setMinLengthRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableMaxLengthRule.isSelected()) {
|
||||||
|
validationConfiguration.setMaxLengthRuleFor(selectedColumn.getValue(), maxLengthSpinner.getValue());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setMaxLengthRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableRegexpRule.isSelected()) {
|
||||||
|
validationConfiguration.setRegexpRuleFor(selectedColumn.getValue(), regexpRuleTextField.getText());
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setRegexpRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableValueOfRule.isSelected()) {
|
||||||
|
validationConfiguration.setValueOfRuleFor(selectedColumn.getValue(), asList(valueOfRuleTextField.getText().split(", ")));
|
||||||
|
} else {
|
||||||
|
validationConfiguration.setValueOfRuleFor(selectedColumn.getValue(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateForm() {
|
||||||
|
|
||||||
|
updateCheckBox(
|
||||||
|
notEmptyRuleCheckBox,
|
||||||
|
validationConfiguration.getNotEmptyRuleFor(getSelectedColumn()),
|
||||||
|
enableNotEmptyRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateCheckBox(
|
||||||
|
integerRuleCheckBox,
|
||||||
|
validationConfiguration.getIntegerRuleFor(getSelectedColumn()),
|
||||||
|
enableIntegerRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateCheckBox(
|
||||||
|
doublerRuleCheckBox,
|
||||||
|
validationConfiguration.getDoubleRuleFor(getSelectedColumn()),
|
||||||
|
enableDoubleRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateCheckBox(
|
||||||
|
alphanumericRuleCheckBox,
|
||||||
|
validationConfiguration.getAlphanumericRuleFor(getSelectedColumn()),
|
||||||
|
enableAlphanumericRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateSpinner(
|
||||||
|
minLengthSpinner,
|
||||||
|
validationConfiguration.getMinLengthRuleFor(getSelectedColumn()),
|
||||||
|
enableMinLengthRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateSpinner(
|
||||||
|
maxLengthSpinner,
|
||||||
|
validationConfiguration.getMaxLengthRuleFor(getSelectedColumn()),
|
||||||
|
enableMaxLengthRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateTextInputControl(
|
||||||
|
dateformatRuleTextField,
|
||||||
|
validationConfiguration.getDateRuleFor(getSelectedColumn()),
|
||||||
|
enableDateRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateTextInputControl(
|
||||||
|
regexpRuleTextField,
|
||||||
|
validationConfiguration.getRegexpRuleFor(getSelectedColumn()),
|
||||||
|
enableRegexpRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateTextInputControl(
|
||||||
|
valueOfRuleTextField,
|
||||||
|
validationConfiguration.getValueOfRuleFor(getSelectedColumn()),
|
||||||
|
enableValueOfRule
|
||||||
|
);
|
||||||
|
|
||||||
|
updateCodeAreaControl(
|
||||||
|
groovyRuleTextArea,
|
||||||
|
validationConfiguration.getGroovyRuleFor(getSelectedColumn()),
|
||||||
|
enableGroovyRule
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCheckBox(CheckBox rule, Boolean value, CheckBox ruleEnabled) {
|
||||||
|
if (value == null) {
|
||||||
|
ruleEnabled.setSelected(false);
|
||||||
|
} else {
|
||||||
|
rule.setSelected(value);
|
||||||
|
ruleEnabled.setSelected(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSpinner(Spinner rule, Integer value, CheckBox ruleEnabled) {
|
||||||
|
if (value == null) {
|
||||||
|
ruleEnabled.setSelected(false);
|
||||||
|
} else {
|
||||||
|
ruleEnabled.setSelected(true);
|
||||||
|
rule.getValueFactory().setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTextInputControl(TextInputControl rule, String value, CheckBox ruleEnabled) {
|
||||||
|
if (value == null) {
|
||||||
|
ruleEnabled.setSelected(false);
|
||||||
|
} else {
|
||||||
|
ruleEnabled.setSelected(true);
|
||||||
|
rule.setText(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTextInputControl(TextInputControl rule, List<String> values, CheckBox ruleEnabled) {
|
||||||
|
if (values == null || values.isEmpty()) {
|
||||||
|
ruleEnabled.setSelected(false);
|
||||||
|
} else {
|
||||||
|
ruleEnabled.setSelected(true);
|
||||||
|
rule.setText(values.stream().collect(joining(", ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) -> {
|
||||||
|
if (!newValue) {
|
||||||
|
rule.setSelected(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSpinner(Spinner rule, CheckBox ruleEnabled) {
|
||||||
|
rule.disableProperty().bind(ruleEnabled.selectedProperty().not());
|
||||||
|
ruleEnabled.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (!newValue) {
|
||||||
|
rule.getValueFactory().setValue(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTextInputControl(TextInputControl rule, CheckBox ruleEnabled) {
|
||||||
|
rule.disableProperty().bind(ruleEnabled.selectedProperty().not());
|
||||||
|
ruleEnabled.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (!newValue) {
|
||||||
|
rule.setText("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,8 +26,7 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.preferences;
|
package ninja.javafx.smartcsv.preferences;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.typesafe.config.ConfigFactory;
|
|
||||||
import ninja.javafx.smartcsv.FileReader;
|
import ninja.javafx.smartcsv.FileReader;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.supercsv.prefs.CsvPreference;
|
import org.supercsv.prefs.CsvPreference;
|
||||||
@@ -35,16 +34,17 @@ import org.supercsv.quote.AlwaysQuoteMode;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static ninja.javafx.smartcsv.preferences.QuoteModeHelper.getQuoteMode;
|
import static ninja.javafx.smartcsv.preferences.QuoteModeHelper.getQuoteMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* file reader for the preferences
|
* file reader for the preferences
|
||||||
*/
|
*/
|
||||||
@Service
|
public class PreferencesFileReader implements FileReader<CsvPreference> {
|
||||||
public class PreferencesFileReader implements FileReader {
|
|
||||||
|
|
||||||
private Config config;
|
private Map config;
|
||||||
private CsvPreference csvPreference;
|
private CsvPreference csvPreference;
|
||||||
|
|
||||||
public PreferencesFileReader() {
|
public PreferencesFileReader() {
|
||||||
@@ -55,15 +55,15 @@ public class PreferencesFileReader implements FileReader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(File filename) throws IOException {
|
public void read(File filename) throws IOException {
|
||||||
config = ConfigFactory.parseFile(filename);
|
config = new GsonBuilder().create().fromJson(new java.io.FileReader(filename), HashMap.class);
|
||||||
|
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
char quoteChar = config.getString("quoteChar").charAt(0);
|
char quoteChar = config.get("quoteChar").toString().charAt(0);
|
||||||
char delimiterChar = config.getString("delimiterChar").charAt(0);
|
char delimiterChar = config.get("delimiterChar").toString().charAt(0);
|
||||||
String endOfLineSymbols = config.getString("endOfLineSymbols");
|
String endOfLineSymbols = config.get("endOfLineSymbols").toString();
|
||||||
boolean surroundingSpacesNeedQuotes = config.getBoolean("surroundingSpacesNeedQuotes");
|
boolean surroundingSpacesNeedQuotes = (Boolean)config.get("surroundingSpacesNeedQuotes");
|
||||||
boolean ignoreEmptyLines = config.getBoolean("ignoreEmptyLines");
|
boolean ignoreEmptyLines = (Boolean)config.get("ignoreEmptyLines");
|
||||||
String quoteMode = config.getString("quoteMode");
|
String quoteMode = config.get("quoteMode").toString();
|
||||||
|
|
||||||
csvPreference = new CsvPreference.Builder(quoteChar, delimiterChar, endOfLineSymbols)
|
csvPreference = new CsvPreference.Builder(quoteChar, delimiterChar, endOfLineSymbols)
|
||||||
.useQuoteMode(getQuoteMode(quoteMode))
|
.useQuoteMode(getQuoteMode(quoteMode))
|
||||||
@@ -73,7 +73,7 @@ public class PreferencesFileReader implements FileReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CsvPreference getCSVpreference() {
|
public CsvPreference getContent() {
|
||||||
return csvPreference;
|
return csvPreference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,8 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.preferences;
|
package ninja.javafx.smartcsv.preferences;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.google.gson.Gson;
|
||||||
import com.typesafe.config.ConfigFactory;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.typesafe.config.ConfigRenderOptions;
|
|
||||||
import ninja.javafx.smartcsv.FileWriter;
|
import ninja.javafx.smartcsv.FileWriter;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.supercsv.prefs.CsvPreference;
|
import org.supercsv.prefs.CsvPreference;
|
||||||
@@ -42,12 +41,11 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* Save preferences to configuration file
|
* Save preferences to configuration file
|
||||||
*/
|
*/
|
||||||
@Service
|
public class PreferencesFileWriter implements FileWriter<CsvPreference> {
|
||||||
public class PreferencesFileWriter implements FileWriter {
|
|
||||||
|
|
||||||
private CsvPreference csvPreference;
|
private CsvPreference csvPreference;
|
||||||
|
|
||||||
public void setCsvPreference(CsvPreference csvPreference) {
|
public void setContent(CsvPreference csvPreference) {
|
||||||
this.csvPreference = csvPreference;
|
this.csvPreference = csvPreference;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,8 +58,8 @@ public class PreferencesFileWriter implements FileWriter {
|
|||||||
preferences.put("ignoreEmptyLines", csvPreference.isIgnoreEmptyLines());
|
preferences.put("ignoreEmptyLines", csvPreference.isIgnoreEmptyLines());
|
||||||
preferences.put("quoteMode", QuoteModeHelper.getQuoteModeName(csvPreference.getQuoteMode()));
|
preferences.put("quoteMode", QuoteModeHelper.getQuoteModeName(csvPreference.getQuoteMode()));
|
||||||
|
|
||||||
Config config = ConfigFactory.parseMap(preferences);
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
Files.write(file.toPath(), config.root().render(ConfigRenderOptions.concise()).getBytes());
|
Files.write(file.toPath(), gson.toJson(preferences).getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* header configuration for the validation
|
||||||
|
*/
|
||||||
|
public class HeaderConfiguration {
|
||||||
|
|
||||||
|
@SerializedName("list")
|
||||||
|
private String[] names ;
|
||||||
|
|
||||||
|
public String[] getNames() {
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNames(String[] names) {
|
||||||
|
this.names = names;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2015 javafx.ninja <info@javafx.ninja>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validation configuration
|
||||||
|
*/
|
||||||
|
public class ValidationConfiguration {
|
||||||
|
|
||||||
|
@SerializedName("headers")
|
||||||
|
private HeaderConfiguration headerConfiguration = new HeaderConfiguration();
|
||||||
|
|
||||||
|
@SerializedName("columns")
|
||||||
|
private Map<String, Map<String, Object>> columnConfigurations = new HashMap<>();
|
||||||
|
|
||||||
|
public String[] headerNames() {
|
||||||
|
if (noHeader()) return null;
|
||||||
|
return headerConfiguration.getNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaderNames(String[] headerNames) {
|
||||||
|
headerConfiguration.setNames(headerNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getIntegerRuleFor(String column) {
|
||||||
|
return (Boolean)getValue(column, "integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getDoubleRuleFor(String column) {
|
||||||
|
return (Boolean)getValue(column, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getNotEmptyRuleFor(String column) {
|
||||||
|
return (Boolean)getValue(column, "not empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMinLengthRuleFor(String column) {
|
||||||
|
return doubleToInteger((Double)getValue(column, "minlength"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMaxLengthRuleFor(String column) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return doubleToInteger((Double)getValue(column, "maxlength"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDateRuleFor(String column) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return (String)getValue(column, "date");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getAlphanumericRuleFor(String column) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return (Boolean)getValue(column, "alphanumeric");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegexpRuleFor(String column) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return (String)getValue(column, "regexp");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getValueOfRuleFor(String column) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return (List<String>)getValue(column, "value of");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroovyRuleFor(String column) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return (String)getValue(column, "groovy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntegerRuleFor(String column, Boolean value) {
|
||||||
|
setValue(column, value, "integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDoubleRuleFor(String column, Boolean value) {
|
||||||
|
setValue(column, value, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotEmptyRuleFor(String column, Boolean value) {
|
||||||
|
setValue(column, value, "not empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinLengthRuleFor(String column, Integer value) {
|
||||||
|
setValue(column, value == null ? null : value.doubleValue(), "minlength");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxLengthRuleFor(String column, Integer value) {
|
||||||
|
setValue(column, value == null ? null : value.doubleValue(), "maxlength");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDateRuleFor(String column, String value) {
|
||||||
|
setValue(column, value, "date");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlphanumericRuleFor(String column, Boolean value) {
|
||||||
|
setValue(column, value, "alphanumeric");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegexpRuleFor(String column, String value) {
|
||||||
|
setValue(column, value, "regexp");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueOfRuleFor(String column, List<String> value) {
|
||||||
|
setValue(column, value, "value of");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroovyRuleFor(String column, String value) {
|
||||||
|
setValue(column, value, "groovy");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setValue(String column, Object value, String key) {
|
||||||
|
if (!columnConfigurations.containsKey(column)) {
|
||||||
|
columnConfigurations.put(column, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null && columnConfigurations.get(column).containsKey(key)) {
|
||||||
|
columnConfigurations.get(column).remove(key);
|
||||||
|
} else {
|
||||||
|
columnConfigurations.get(column).put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getValue(String column, String key) {
|
||||||
|
if (noRulesFor(column)) return null;
|
||||||
|
return columnConfigurations.get(column).get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean noHeader() {
|
||||||
|
return headerConfiguration == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean noRulesFor(String column) {
|
||||||
|
return columnConfigurations == null || columnConfigurations.get(column) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer doubleToInteger(Double value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
return (int)Math.round(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,8 +26,7 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.validation;
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.typesafe.config.ConfigFactory;
|
|
||||||
import ninja.javafx.smartcsv.FileReader;
|
import ninja.javafx.smartcsv.FileReader;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -37,17 +36,16 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* This class loads the constraints as json config
|
* This class loads the constraints as json config
|
||||||
*/
|
*/
|
||||||
@Service
|
public class ValidationFileReader implements FileReader<ValidationConfiguration> {
|
||||||
public class ValidationFileReader implements FileReader {
|
|
||||||
|
|
||||||
private Config config;
|
private ValidationConfiguration config;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(File file) throws IOException {
|
public void read(File file) throws IOException {
|
||||||
config = ConfigFactory.parseFile(file);
|
config = new GsonBuilder().create().fromJson(new java.io.FileReader(file), ValidationConfiguration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Validator getValidator() {
|
public ValidationConfiguration getContent() {
|
||||||
return new Validator(config);
|
return config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,47 +24,31 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ninja.javafx.smartcsv.fx.list;
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
import javafx.scene.control.ListCell;
|
import com.google.gson.Gson;
|
||||||
import javafx.scene.text.Text;
|
import com.google.gson.GsonBuilder;
|
||||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
import ninja.javafx.smartcsv.FileWriter;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ResourceBundle;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell to show the error text
|
* file writer for the validation configuration
|
||||||
*/
|
*/
|
||||||
public class ValidationErrorListCell extends ListCell<ValidationError> {
|
public class ValidationFileWriter implements FileWriter<ValidationConfiguration> {
|
||||||
|
|
||||||
private ResourceBundle resourceBundle;
|
private ValidationConfiguration validationConfiguration;
|
||||||
|
|
||||||
public ValidationErrorListCell(ResourceBundle resourceBundle) {
|
public void setContent(ValidationConfiguration validationConfiguration) {
|
||||||
this.resourceBundle = resourceBundle;
|
this.validationConfiguration = validationConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(ValidationError validationError, boolean empty) {
|
public void write(File file) throws IOException {
|
||||||
super.updateItem(validationError, empty);
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
if (empty) {
|
Files.write(file.toPath(), gson.toJson(validationConfiguration).getBytes());
|
||||||
clearContent();
|
|
||||||
} else {
|
|
||||||
addContent(validationError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void clearContent() {
|
|
||||||
setText(null);
|
|
||||||
setGraphic(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addContent(ValidationError validationError) {
|
|
||||||
setText(null);
|
|
||||||
Text text = new Text(getI18nValidatioMessage(resourceBundle, validationError));
|
|
||||||
text.setWrappingWidth(180);
|
|
||||||
setGraphic(text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.validation;
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
|
||||||
import groovy.lang.Binding;
|
import groovy.lang.Binding;
|
||||||
import groovy.lang.GroovyShell;
|
import groovy.lang.GroovyShell;
|
||||||
import groovy.lang.Script;
|
import groovy.lang.Script;
|
||||||
@@ -49,7 +48,7 @@ public class Validator {
|
|||||||
// member variables
|
// member variables
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private Config validationConfig;
|
private ValidationConfiguration validationConfig;
|
||||||
private GroovyShell shell = new GroovyShell();
|
private GroovyShell shell = new GroovyShell();
|
||||||
private Map<String, Script> scriptCache = new HashMap<>();
|
private Map<String, Script> scriptCache = new HashMap<>();
|
||||||
|
|
||||||
@@ -62,13 +61,13 @@ public class Validator {
|
|||||||
* JSON configuration for this validator
|
* JSON configuration for this validator
|
||||||
* @param validationConfig
|
* @param validationConfig
|
||||||
*/
|
*/
|
||||||
public Validator(Config validationConfig) {
|
public Validator(ValidationConfiguration validationConfig) {
|
||||||
this.validationConfig = validationConfig;
|
this.validationConfig = validationConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// oublic methods
|
// public methods
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,42 +78,39 @@ public class Validator {
|
|||||||
*/
|
*/
|
||||||
public ValidationError isValid(String column, String value, Integer lineNumber) {
|
public ValidationError isValid(String column, String value, Integer lineNumber) {
|
||||||
ValidationError result = null;
|
ValidationError result = null;
|
||||||
if (validationConfig != null) {
|
if (hasConfig()) {
|
||||||
Config columnSectionConfig = getColumnSectionConfig();
|
|
||||||
if (columnSectionConfig != null) {
|
|
||||||
Config columnConfig = getColumnConfig(columnSectionConfig, column);
|
|
||||||
if (columnConfig != null) {
|
|
||||||
|
|
||||||
ValidationError error = ValidationError.withLineNumber(lineNumber);
|
ValidationError error = ValidationError.withLineNumber(lineNumber);
|
||||||
checkBlankOrNull(columnConfig, value, error);
|
checkBlankOrNull(column, value, error);
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
checkRegularExpression(columnConfig, value, error);
|
checkRegularExpression(column, value, error);
|
||||||
checkAlphaNumeric(columnConfig, value, error);
|
checkAlphaNumeric(column, value, error);
|
||||||
checkDate(columnConfig, value, error);
|
checkDate(column, value, error);
|
||||||
checkMaxLength(columnConfig, value, error);
|
checkMaxLength(column, value, error);
|
||||||
checkMinLength(columnConfig, value, error);
|
checkMinLength(column, value, error);
|
||||||
checkInteger(columnConfig, value, error);
|
checkInteger(column, value, error);
|
||||||
checkGroovy(column, columnConfig, value, error);
|
checkGroovy(column, value, error);
|
||||||
checkValueOf(columnConfig, value, error);
|
checkValueOf(column, value, error);
|
||||||
checkDouble(columnConfig, value, error);
|
checkDouble(column, value, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error.isEmpty()) {
|
if (!error.isEmpty()) {
|
||||||
result = error;
|
result = error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasConfig() {
|
||||||
|
return validationConfig != null;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// private methods
|
// private methods
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void checkGroovy(String column, Config columnConfig, String value, ValidationError error) {
|
private void checkGroovy(String column, String value, ValidationError error) {
|
||||||
String groovyScript = getString(columnConfig, "groovy");
|
String groovyScript = validationConfig.getGroovyRuleFor(column);
|
||||||
if (groovyScript != null) {
|
if (groovyScript != null) {
|
||||||
|
|
||||||
Script script = scriptCache.get(column);
|
Script script = scriptCache.get(column);
|
||||||
@@ -149,43 +145,42 @@ public class Validator {
|
|||||||
return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true");
|
return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkValueOf(Config columnConfig, String value, ValidationError error) {
|
private void checkValueOf(String column, String value, ValidationError error) {
|
||||||
List<String> stringList = getStringList(columnConfig, "value of");
|
List<String> values = validationConfig.getValueOfRuleFor(column);
|
||||||
if (stringList != null) {
|
if (values != null) {
|
||||||
if (!stringList.contains(value)) {
|
if (!values.contains(value)) {
|
||||||
String commaSeparated = stringList.stream().collect(joining(", "));
|
String commaSeparated = values.stream().collect(joining(", "));
|
||||||
error.add("validation.message.value.of", value, commaSeparated);
|
error.add("validation.message.value.of", value, commaSeparated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkBlankOrNull(Config columnConfig, String value, ValidationError error) {
|
private void checkBlankOrNull(String column, String value, ValidationError error) {
|
||||||
if (getBoolean(columnConfig, "not empty")) {
|
if (validationConfig.getNotEmptyRuleFor(column) != null && validationConfig.getNotEmptyRuleFor(column)) {
|
||||||
if (isBlankOrNull(value)) {
|
if (isBlankOrNull(value)) {
|
||||||
error.add("validation.message.not.empty");
|
error.add("validation.message.not.empty");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkInteger(Config columnConfig, String value, ValidationError error) {
|
private void checkInteger(String column, String value, ValidationError error) {
|
||||||
if (getBoolean(columnConfig, "integer")) {
|
if (validationConfig.getIntegerRuleFor(column) != null && validationConfig.getIntegerRuleFor(column)) {
|
||||||
if (!isInt(value)) {
|
if (!isInt(value)) {
|
||||||
error.add("validation.message.integer");
|
error.add("validation.message.integer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDouble(Config columnConfig, String value, ValidationError error) {
|
private void checkDouble(String column, String value, ValidationError error) {
|
||||||
if (getBoolean(columnConfig, "double")) {
|
if (validationConfig.getDoubleRuleFor(column) != null && validationConfig.getDoubleRuleFor(column)) {
|
||||||
if (!isDouble(value)) {
|
if (!isDouble(value)) {
|
||||||
error.add("validation.message.double");
|
error.add("validation.message.double");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkMinLength(String column, String value, ValidationError error) {
|
||||||
private void checkMinLength(Config columnConfig, String value, ValidationError error) {
|
Integer minLength = validationConfig.getMinLengthRuleFor(column);
|
||||||
Integer minLength = getInteger(columnConfig, "minlength");
|
|
||||||
if (minLength != null) {
|
if (minLength != null) {
|
||||||
if (!minLength(value, minLength)) {
|
if (!minLength(value, minLength)) {
|
||||||
error.add("validation.message.min.length", minLength.toString());
|
error.add("validation.message.min.length", minLength.toString());
|
||||||
@@ -193,8 +188,8 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkMaxLength(Config columnConfig, String value, ValidationError error) {
|
private void checkMaxLength(String column, String value, ValidationError error) {
|
||||||
Integer maxLength = getInteger(columnConfig, "maxlength");
|
Integer maxLength = validationConfig.getMaxLengthRuleFor(column);
|
||||||
if (maxLength != null) {
|
if (maxLength != null) {
|
||||||
if (!maxLength(value, maxLength)) {
|
if (!maxLength(value, maxLength)) {
|
||||||
error.add("validation.message.max.length", maxLength.toString());
|
error.add("validation.message.max.length", maxLength.toString());
|
||||||
@@ -202,8 +197,8 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDate(Config columnConfig, String value, ValidationError error) {
|
private void checkDate(String column, String value, ValidationError error) {
|
||||||
String dateformat = getString(columnConfig, "date");
|
String dateformat = validationConfig.getDateRuleFor(column);
|
||||||
if (dateformat != null && !dateformat.trim().isEmpty()) {
|
if (dateformat != null && !dateformat.trim().isEmpty()) {
|
||||||
if (!isDate(value, dateformat, true)) {
|
if (!isDate(value, dateformat, true)) {
|
||||||
error.add("validation.message.date.format", dateformat);
|
error.add("validation.message.date.format", dateformat);
|
||||||
@@ -211,16 +206,16 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAlphaNumeric(Config columnConfig, String value, ValidationError error) {
|
private void checkAlphaNumeric(String column, String value, ValidationError error) {
|
||||||
if (getBoolean(columnConfig, "alphanumeric")) {
|
if (validationConfig.getAlphanumericRuleFor(column) != null && validationConfig.getAlphanumericRuleFor(column)) {
|
||||||
if (!matchRegexp(value, "[0-9a-zA-Z]*")) {
|
if (!matchRegexp(value, "[0-9a-zA-Z]*")) {
|
||||||
error.add("validation.message.alphanumeric");
|
error.add("validation.message.alphanumeric");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRegularExpression(Config columnConfig, String value, ValidationError error) {
|
private void checkRegularExpression(String column, String value, ValidationError error) {
|
||||||
String regexp = getString(columnConfig, "regexp");
|
String regexp = validationConfig.getRegexpRuleFor(column);
|
||||||
if (regexp != null && !regexp.trim().isEmpty()) {
|
if (regexp != null && !regexp.trim().isEmpty()) {
|
||||||
if (!matchRegexp(value, regexp)) {
|
if (!matchRegexp(value, regexp)) {
|
||||||
error.add("validation.message.regexp", regexp);
|
error.add("validation.message.regexp", regexp);
|
||||||
@@ -228,53 +223,25 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Config getColumnSectionConfig() {
|
|
||||||
return validationConfig.hasPath("columns") ? validationConfig.getConfig("columns") : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Config getColumnConfig(Config columnSectionConfig, String column) {
|
|
||||||
return columnSectionConfig.hasPath(column) ? columnSectionConfig.getConfig(column) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String getString(Config columnConfig, String path) {
|
|
||||||
return columnConfig.hasPath(path) ? columnConfig.getString(path) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer getInteger(Config columnConfig, String path) {
|
|
||||||
return columnConfig.hasPath(path) ? columnConfig.getInt(path) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean getBoolean(Config columnConfig, String path) {
|
|
||||||
return columnConfig.hasPath(path) && columnConfig.getBoolean(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getStringList(Config columnConfig, String path) {
|
|
||||||
return columnConfig.hasPath(path) ? columnConfig.getStringList(path) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValidationError isHeaderValid(String[] headerNames) {
|
public ValidationError isHeaderValid(String[] headerNames) {
|
||||||
ValidationError result = null;
|
ValidationError result = null;
|
||||||
if (validationConfig != null) {
|
if (validationConfig != null) {
|
||||||
if (validationConfig.hasPath("headers")) {
|
String[] headerNamesConfig = validationConfig.headerNames();
|
||||||
Config headerSectionConfig = validationConfig.getConfig("headers");
|
if (headerNamesConfig != null) {
|
||||||
List<String> headerConfig = getStringList(headerSectionConfig, "list");
|
if (headerNames.length != headerNamesConfig.length) {
|
||||||
if (headerConfig != null) {
|
|
||||||
if (headerNames.length != headerConfig.size()) {
|
|
||||||
result = ValidationError.withoutLineNumber().add("validation.message.header.length",
|
result = ValidationError.withoutLineNumber().add("validation.message.header.length",
|
||||||
Integer.toString(headerNames.length),
|
Integer.toString(headerNames.length),
|
||||||
Integer.toString(headerConfig.size()));
|
Integer.toString(headerNamesConfig.length));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidationError error = ValidationError.withoutLineNumber();
|
ValidationError error = ValidationError.withoutLineNumber();
|
||||||
|
|
||||||
for(int i=0; i<headerConfig.size(); i++) {
|
for(int i=0; i<headerNamesConfig.length; i++) {
|
||||||
String header = headerConfig.get(i);
|
if (!headerNamesConfig[i].equals(headerNames[i])) {
|
||||||
if (!header.equals(headerNames[i])) {
|
|
||||||
error.add("validation.message.header.match",
|
error.add("validation.message.header.match",
|
||||||
Integer.toString(i),
|
Integer.toString(i),
|
||||||
header,
|
headerNamesConfig[i],
|
||||||
headerNames[i]);
|
headerNames[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +250,10 @@ public class Validator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearScriptCache() {
|
||||||
|
scriptCache.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/main/resources/log4j2.xml
Normal file
26
src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="error">
|
||||||
|
|
||||||
|
<Properties>
|
||||||
|
<Property name="filename">smartcsv.log</Property>
|
||||||
|
</Properties>
|
||||||
|
<ThresholdFilter level="trace"/>
|
||||||
|
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||||
|
</Console>
|
||||||
|
|
||||||
|
<File name="File" fileName="${filename}" bufferedIO="true">
|
||||||
|
<PatternLayout>
|
||||||
|
<pattern>%d %p %C{1.} [%t] %m%n</pattern>
|
||||||
|
</PatternLayout>
|
||||||
|
</File>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="error">
|
||||||
|
<AppenderRef ref="File"/>
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
@@ -1,14 +1,37 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.scene.text.*?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import java.lang.*?>
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
<?import javafx.geometry.*?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="600.0" spacing="4.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="600.0" spacing="4.0" xmlns="http://javafx.com/javafx/8.0.66" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<children>
|
<children>
|
||||||
|
<Label text="SmartCSV.fx">
|
||||||
|
<font>
|
||||||
|
<Font size="18.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets top="8.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="A simple JavaFX application to load, save and edit a CSV file and provide a JSON configuration for columns " wrapText="true" />
|
||||||
|
<Label text="to check the values in the columns." />
|
||||||
|
<Label text="Thanks for contributions:">
|
||||||
|
<font>
|
||||||
|
<Font size="18.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets top="8.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="AOE Takashi (https://github.com/aoetk)" />
|
||||||
<Label text="3rd party software">
|
<Label text="3rd party software">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets top="8.0" />
|
<Insets top="8.0" />
|
||||||
@@ -19,7 +42,7 @@
|
|||||||
</Label>
|
</Label>
|
||||||
<ScrollPane fitToHeight="true" fitToWidth="true">
|
<ScrollPane fitToHeight="true" fitToWidth="true">
|
||||||
<content>
|
<content>
|
||||||
<GridPane hgap="4.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" vgap="4.0">
|
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
||||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
|
||||||
@@ -33,6 +56,9 @@
|
|||||||
<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 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" />
|
||||||
@@ -45,12 +71,18 @@
|
|||||||
<Hyperlink onAction="#openLinkInBrowser" text="https://spring.io/" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
<Hyperlink onAction="#openLinkInBrowser" text="https://spring.io/" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
<Label text="supercsv" GridPane.rowIndex="4" />
|
<Label text="supercsv" GridPane.rowIndex="4" />
|
||||||
<Hyperlink onAction="#openLinkInBrowser" text="http://super-csv.github.io/super-csv/" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
<Hyperlink onAction="#openLinkInBrowser" text="http://super-csv.github.io/super-csv/" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
<Label text="config" GridPane.rowIndex="5" />
|
<Label text="Gson" GridPane.rowIndex="5" />
|
||||||
<Hyperlink onAction="#openLinkInBrowser" text="https://github.com/typesafehub/config" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
<Hyperlink onAction="#openLinkInBrowser" text="https://github.com/google/gson/" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||||
<Label text="commons-validator" GridPane.rowIndex="6" />
|
<Label text="commons-validator" GridPane.rowIndex="6" />
|
||||||
<Hyperlink onAction="#openLinkInBrowser" text="https://commons.apache.org/proper/commons-validator/" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
<Hyperlink onAction="#openLinkInBrowser" text="https://commons.apache.org/proper/commons-validator/" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
<Label text="fontawesomefx" GridPane.rowIndex="7" />
|
<Label text="fontawesomefx" GridPane.rowIndex="7" />
|
||||||
<Hyperlink onAction="#openLinkInBrowser" text="https://bitbucket.org/Jerady/fontawesomefx" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
<Hyperlink onAction="#openLinkInBrowser" text="https://bitbucket.org/Jerady/fontawesomefx" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
||||||
|
<Label text="ControlsFX" 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" />
|
||||||
|
<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" />
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
application.name = SmartCSV.fx
|
application.name = SmartCSV.fx
|
||||||
application.version = 0.3
|
application.version = 0.4
|
||||||
|
|
||||||
# fxml views
|
# fxml views
|
||||||
fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml
|
fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml
|
||||||
fxml.smartcvs.about.view = /ninja/javafx/smartcsv/fx/about/about.fxml
|
fxml.smartcvs.about.view = /ninja/javafx/smartcsv/fx/about/about.fxml
|
||||||
fxml.smartcvs.preferences.view = /ninja/javafx/smartcsv/fx/preferences/preferences.fxml
|
fxml.smartcvs.preferences.view = /ninja/javafx/smartcsv/fx/preferences/preferences.fxml
|
||||||
|
fxml.smartcvs.validation.editor.view = /ninja/javafx/smartcsv/fx/validation/validationEditor.fxml
|
||||||
|
|
||||||
# resources
|
# resources
|
||||||
resource.main = ninja.javafx.smartcsv.fx.smartcsv
|
resource.main = ninja.javafx.smartcsv.fx.smartcsv
|
||||||
@@ -15,36 +15,199 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.open-icon {
|
.open-icon {
|
||||||
-glyph-name: "FILE_TEXT_ALT";
|
-glyph-name: "FILE_IMPORT";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-document-icon {
|
||||||
|
-glyph-name: "FILE_DOCUMENT";
|
||||||
|
-glyph-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-icon {
|
.config-icon {
|
||||||
-glyph-name: "CHECK";
|
-glyph-name: "CLIPBOARD";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-config-icon {
|
||||||
|
-glyph-name: "STAR";
|
||||||
|
-glyph-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-config-icon {
|
||||||
|
-glyph-name: "ARROW_UP_BOLD";
|
||||||
|
-glyph-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-config-icon {
|
||||||
|
-glyph-name: "ARROW_DOWN_BOLD";
|
||||||
|
-glyph-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-icon {
|
.save-icon {
|
||||||
-glyph-name: "FLOPPY_ALT";
|
-glyph-name: "FILE_EXPORT";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.exit-icon {
|
.exit-icon {
|
||||||
-glyph-name: "SIGN_OUT";
|
-glyph-name: "EXIT_TO_APP";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-icon {
|
.info-icon {
|
||||||
-glyph-name: "INFO";
|
-glyph-name: "INFORMATION_OUTLINE";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-title-icon {
|
.config-check-icon {
|
||||||
-glyph-name: "EXCLAMATION_TRIANGLE";
|
-glyph-name: "CLIPBOARD_CHECK";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferences-icon {
|
.preferences-icon {
|
||||||
-glyph-name: "COG";
|
-glyph-name: "SETTINGS";
|
||||||
-glyph-size: 14px;
|
-glyph-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
-glyph-name: "TABLE_ROW_REMOVE";
|
||||||
|
-glyph-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
-glyph-name: "TABLE_ROW_PLUS_AFTER";
|
||||||
|
-glyph-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* toolbar customization based on http://fxexperience.com/2012/02/customized-segmented-toolbar-buttons/ */
|
||||||
|
|
||||||
|
#background {
|
||||||
|
-light-black: rgb(74, 75, 78);
|
||||||
|
-dark-highlight: rgb(87, 89, 92);
|
||||||
|
-dark-black: rgb(39, 40, 40);
|
||||||
|
-darkest-black: rgb(5, 5, 5);
|
||||||
|
-mid-gray: rgb(216, 222, 227);
|
||||||
|
-fx-background-color: -mid-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar {
|
||||||
|
-fx-base: -dark-black;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-background-color:
|
||||||
|
linear-gradient(to bottom, derive(-fx-base,-30%), derive(-fx-base,-60%)),
|
||||||
|
linear-gradient(to bottom, -light-black 2%, -dark-black 98%);
|
||||||
|
-fx-background-insets: 0, 0 0 1 0;
|
||||||
|
-fx-padding: .6em 0.416667em .6em 0.416667em;
|
||||||
|
-fx-effect: dropshadow(two-pass-box,black,5,.2,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .open-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .config-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item .load-config-icon,
|
||||||
|
.menu-item .save-config-icon,
|
||||||
|
.menu-item .create-config-icon
|
||||||
|
{
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .save-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .preferences-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .exit-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .delete-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-bar .add-icon {
|
||||||
|
-glyph-size: 16px;
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-button-bar .button {
|
||||||
|
-fx-background-color:
|
||||||
|
-darkest-black,
|
||||||
|
-dark-highlight,
|
||||||
|
linear-gradient(to bottom, -light-black 2%, -dark-black 98%);
|
||||||
|
-fx-background-insets: 0, 1 1 1 0, 2 1 1 1;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
-fx-padding: 0.4em 1.833333em 0.4em 1.833333em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-button-bar .button.first {
|
||||||
|
-fx-background-insets: 0, 1, 2 1 1 1;
|
||||||
|
-fx-background-radius: 3 0 0 3, 2 0 0 2, 2 0 0 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-button-bar .button.last {
|
||||||
|
-fx-background-insets: 0, 1 1 1 0, 2 1 1 1;
|
||||||
|
-fx-background-radius: 0 3 3 0, 0 2 2 0, 0 2 2 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segmented-button-bar .button:pressed {
|
||||||
|
-fx-background-color:
|
||||||
|
-darkest-black,
|
||||||
|
rgb(55, 57, 58),
|
||||||
|
linear-gradient(to top, -light-black 2%, -dark-black 98%);
|
||||||
|
}
|
||||||
|
.tool-bar .spacer {
|
||||||
|
-fx-padding: 0 5.417em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
-fx-background-radius: 2 2 2 2;
|
||||||
|
-fx-background-color: linear-gradient(#eeeeee, #aaaaaa);
|
||||||
|
-fx-text-fill: black;
|
||||||
|
-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;
|
||||||
}
|
}
|
||||||
@@ -1,101 +1,264 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import de.jensd.fx.glyphs.fontawesome.*?>
|
<?import de.jensd.fx.glyphs.GlyphsStack?>
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
|
||||||
<?import java.net.URL?>
|
<?import java.net.URL?>
|
||||||
<BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
|
<?import javafx.geometry.Insets?>
|
||||||
minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.66"
|
<?import javafx.scene.control.Button?>
|
||||||
xmlns:fx="http://javafx.com/fxml/1">
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.Menu?>
|
||||||
|
<?import javafx.scene.control.MenuBar?>
|
||||||
|
<?import javafx.scene.control.MenuItem?>
|
||||||
|
<?import javafx.scene.control.SeparatorMenuItem?>
|
||||||
|
<?import javafx.scene.control.TableView?>
|
||||||
|
<?import javafx.scene.control.ToolBar?>
|
||||||
|
<?import javafx.scene.control.Tooltip?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
|
||||||
|
<BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="1000.0" minHeight="700.0" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<top>
|
<top>
|
||||||
<VBox prefWidth="100.0" BorderPane.alignment="CENTER">
|
<VBox id="background" prefWidth="100.0" BorderPane.alignment="CENTER">
|
||||||
<children>
|
<children>
|
||||||
<MenuBar>
|
<MenuBar useSystemMenuBar="true">
|
||||||
<menus>
|
<menus>
|
||||||
<Menu mnemonicParsing="false" text="%menu.file">
|
<Menu mnemonicParsing="false" text="%menu.file">
|
||||||
<graphic>
|
|
||||||
<FontAwesomeIconView styleClass="save-icon"/>
|
|
||||||
</graphic>
|
|
||||||
<items>
|
<items>
|
||||||
<MenuItem mnemonicParsing="false" onAction="#openCsv" text="%menu.open.csv">
|
<MenuItem mnemonicParsing="false" onAction="#openCsv" text="%menu.open.csv">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="open-icon"/>
|
<MaterialDesignIconView styleClass="open-icon" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem mnemonicParsing="false" onAction="#openConfig" text="%menu.open.config">
|
<MenuItem fx:id="saveMenuItem" disable="true" mnemonicParsing="false" onAction="#saveCsv" text="%menu.save">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="config-icon"/>
|
<MaterialDesignIconView styleClass="save-icon" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem fx:id="saveAsMenuItem" disable="true" mnemonicParsing="false" onAction="#saveAsCsv" text="%menu.save.as">
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="save-icon" />
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
<SeparatorMenuItem mnemonicParsing="false" />
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
<MenuItem fx:id="saveMenuItem" mnemonicParsing="false" onAction="#saveCsv" text="%menu.save" disable="true">
|
<MenuItem fx:id="createConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#createConfig" text="%menu.create.config">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="save-icon"/>
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="create-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem fx:id="saveAsMenuItem" mnemonicParsing="false" onAction="#saveAsCsv" text="%menu.save.as" disable="true">
|
<MenuItem fx:id="loadConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#openConfig" text="%menu.open.config">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="save-icon"/>
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="load-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem fx:id="saveConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#saveConfig" text="%menu.save.config">
|
||||||
|
<graphic>
|
||||||
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="save-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem fx:id="saveAsConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#saveAsConfig" text="%menu.save.as.config">
|
||||||
|
<graphic>
|
||||||
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="save-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
<SeparatorMenuItem mnemonicParsing="false" />
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
<MenuItem mnemonicParsing="false" onAction="#preferences" text="%menu.preferences">
|
<MenuItem mnemonicParsing="false" onAction="#preferences" text="%menu.preferences">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="preferences-icon"/>
|
<MaterialDesignIconView styleClass="preferences-icon" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<SeparatorMenuItem mnemonicParsing="false" />
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
<MenuItem mnemonicParsing="false" onAction="#close" text="%menu.close">
|
<MenuItem mnemonicParsing="false" onAction="#close" text="%menu.close">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="exit-icon"/>
|
<MaterialDesignIconView styleClass="exit-icon" />
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
<Menu mnemonicParsing="false" text="%menu.edit">
|
||||||
|
<items>
|
||||||
|
<MenuItem fx:id="deleteRowMenuItem" disable="true" mnemonicParsing="false" onAction="#deleteRow" text="%menu.delete.row">
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="delete-icon" />
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem fx:id="addRowMenuItem" disable="true" mnemonicParsing="false" onAction="#addRow" text="%menu.add.row">
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="add-icon" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</items>
|
</items>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu mnemonicParsing="false" text="%menu.help">
|
<Menu mnemonicParsing="false" text="%menu.help">
|
||||||
<graphic>
|
|
||||||
<FontAwesomeIconView styleClass="info-icon"/>
|
|
||||||
</graphic>
|
|
||||||
<items>
|
<items>
|
||||||
<MenuItem mnemonicParsing="false" onAction="#about" text="%menu.about">
|
<MenuItem mnemonicParsing="false" onAction="#about" text="%menu.about">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesomeIconView styleClass="info-icon"/>
|
<MaterialDesignIconView styleClass="info-icon" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</items>
|
</items>
|
||||||
</Menu>
|
</Menu>
|
||||||
</menus>
|
</menus>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
|
<ToolBar prefHeight="40.0" prefWidth="200.0">
|
||||||
|
<HBox styleClass="segmented-button-bar">
|
||||||
|
<Button mnemonicParsing="false" onAction="#openCsv" styleClass="first">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.open.csv" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="open-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="saveButton" disable="true" mnemonicParsing="false" onAction="#saveCsv">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.save" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="save-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="saveAsButton" disable="true" mnemonicParsing="false" onAction="#saveAsCsv" styleClass="last" text="...">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.save.as" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="save-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
<Region styleClass="spacer" />
|
||||||
|
<HBox styleClass="segmented-button-bar">
|
||||||
|
<Button fx:id="createConfigButton" disable="true" mnemonicParsing="false" onAction="#createConfig">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.create.config" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="create-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="loadConfigButton" disable="true" mnemonicParsing="false" onAction="#openConfig">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.open.config" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="load-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="saveConfigButton" disable="true" mnemonicParsing="false" onAction="#saveConfig">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.save.config" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="save-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="saveAsConfigButton" disable="true" mnemonicParsing="false" onAction="#saveAsConfig" styleClass="last" text="...">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.save.as.config" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<GlyphsStack>
|
||||||
|
<children>
|
||||||
|
<MaterialDesignIconView styleClass="config-icon" />
|
||||||
|
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="save-config-icon" />
|
||||||
|
</children>
|
||||||
|
</GlyphsStack>
|
||||||
|
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
<Region styleClass="spacer" />
|
||||||
|
<HBox styleClass="segmented-button-bar">
|
||||||
|
<Button fx:id="deleteRowButton" disable="true" mnemonicParsing="false" onAction="#deleteRow" styleClass="first">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.delete.row" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="delete-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="addRowButton" disable="true" mnemonicParsing="false" onAction="#addRow" styleClass="last">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.add.row" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="add-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
<Region styleClass="spacer" />
|
||||||
|
<HBox styleClass="segmented-button-bar">
|
||||||
|
<Button mnemonicParsing="false" onAction="#preferences" styleClass="first">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.preferences" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="preferences-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button mnemonicParsing="false" onAction="#close" styleClass="last">
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%menu.close" />
|
||||||
|
</tooltip>
|
||||||
|
<graphic>
|
||||||
|
<MaterialDesignIconView styleClass="exit-icon" />
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
</ToolBar>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<SplitPane dividerPositions="0.8" prefHeight="160.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
|
||||||
<items>
|
|
||||||
<AnchorPane fx:id="tableWrapper">
|
<AnchorPane fx:id="tableWrapper">
|
||||||
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0"
|
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" BorderPane.alignment="CENTER">
|
||||||
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
|
||||||
BorderPane.alignment="CENTER">
|
|
||||||
<columns>
|
<columns>
|
||||||
</columns>
|
</columns>
|
||||||
</TableView>
|
</TableView>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
<VBox>
|
|
||||||
<children>
|
|
||||||
<Label text="%title.validation.errors">
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
|
|
||||||
</padding>
|
|
||||||
<graphic>
|
|
||||||
<FontAwesomeIconView styleClass="error-title-icon"/>
|
|
||||||
</graphic>
|
|
||||||
</Label>
|
|
||||||
<ListView fx:id="errorList" minWidth="10.0" prefWidth="200.0" VBox.vgrow="ALWAYS"/>
|
|
||||||
</children>
|
|
||||||
</VBox>
|
|
||||||
</items>
|
|
||||||
</SplitPane>
|
|
||||||
</center>
|
</center>
|
||||||
<left>
|
<left>
|
||||||
</left>
|
</left>
|
||||||
@@ -109,8 +272,8 @@
|
|||||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0"/>
|
|
||||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
|
||||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
@@ -118,17 +281,15 @@
|
|||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<FontAwesomeIconView styleClass="open-icon" GridPane.hgrow="NEVER"/>
|
<MaterialDesignIconView styleClass="file-document-icon" GridPane.hgrow="NEVER" />
|
||||||
<Label text="%stateline.csv" GridPane.columnIndex="1" GridPane.hgrow="NEVER" />
|
<Label text="%stateline.csv" GridPane.columnIndex="1" GridPane.hgrow="NEVER" />
|
||||||
<Label fx:id="csvName" GridPane.columnIndex="2" GridPane.hgrow="ALWAYS" />
|
<Label fx:id="csvName" GridPane.columnIndex="2" GridPane.hgrow="ALWAYS" />
|
||||||
<Label text="%stateline.state" GridPane.columnIndex="3" GridPane.hgrow="NEVER"/>
|
<MaterialDesignIconView styleClass="config-check-icon" GridPane.columnIndex="3" GridPane.hgrow="NEVER" />
|
||||||
<Label fx:id="stateName" GridPane.columnIndex="4" GridPane.hgrow="ALWAYS"/>
|
<Label text="%stateline.configuration" GridPane.columnIndex="4" GridPane.hgrow="NEVER" />
|
||||||
<FontAwesomeIconView styleClass="open-icon" GridPane.columnIndex="5" GridPane.hgrow="NEVER"/>
|
<Label fx:id="configurationName" GridPane.columnIndex="5" GridPane.hgrow="ALWAYS" />
|
||||||
<Label text="%stateline.configuration" GridPane.columnIndex="6" GridPane.hgrow="NEVER"/>
|
|
||||||
<Label fx:id="configurationName" GridPane.columnIndex="7" GridPane.hgrow="ALWAYS"/>
|
|
||||||
</children>
|
</children>
|
||||||
<BorderPane.margin>
|
<BorderPane.margin>
|
||||||
<Insets top="4.0" left="8.0" bottom="4.0" right="8.0"/>
|
<Insets bottom="4.0" left="8.0" right="8.0" top="4.0" />
|
||||||
</BorderPane.margin>
|
</BorderPane.margin>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
</bottom>
|
</bottom>
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
menu.open.csv = Open CSV File
|
menu.open.csv = Open CSV File
|
||||||
menu.open.config = Open Validation Config
|
menu.open.config = Open Validation Config
|
||||||
|
menu.create.config = Create Validation Config
|
||||||
menu.save = Save
|
menu.save = Save
|
||||||
menu.save.as = Save As ...
|
menu.save.as = Save As ...
|
||||||
|
menu.save.config = Save Validation Config
|
||||||
|
menu.save.as.config = Save Validation Config as ...
|
||||||
menu.close = Close
|
menu.close = Close
|
||||||
menu.about = About
|
menu.about = About
|
||||||
menu.file = File
|
menu.file = File
|
||||||
menu.edit = Edit
|
menu.edit = Edit
|
||||||
menu.help = Help
|
menu.help = Help
|
||||||
menu.preferences = Preferences
|
menu.preferences = Preferences
|
||||||
|
menu.delete.row = Delete row
|
||||||
|
menu.add.row = Add row
|
||||||
|
|
||||||
title.validation.errors = Validation Errors:
|
title.validation.errors = Validation Errors:
|
||||||
|
|
||||||
stateline.csv = CSV:
|
stateline.csv = CSV:
|
||||||
@@ -45,3 +51,21 @@ validation.message.regexp = does not match {0}
|
|||||||
validation.message.header.length = number of headers is not correct! there are {0} but there should be {1}
|
validation.message.header.length = number of headers is not correct! there are {0} but there should be {1}
|
||||||
validation.message.header.match = header number {0} does not match "{1}" should be "{3}"
|
validation.message.header.match = header number {0} does not match "{1}" should be "{3}"
|
||||||
validation.message.value.of = Value {0} is not part of this list {1}
|
validation.message.value.of = Value {0} is not part of this list {1}
|
||||||
|
|
||||||
|
validation.rule.label.not_empty = not empty:
|
||||||
|
validation.rule.label.integer = integer:
|
||||||
|
validation.rule.label.double = double:
|
||||||
|
validation.rule.label.minlength = minimum length:
|
||||||
|
validation.rule.label.maxlength = maximum length:
|
||||||
|
validation.rule.label.dateformat = date format:
|
||||||
|
validation.rule.label.alphanumeric = alphanumeric:
|
||||||
|
validation.rule.label.regexp = regular expression:
|
||||||
|
validation.rule.label.value_of = value in list:
|
||||||
|
validation.rule.label.groovy = groovy:
|
||||||
|
validation.rules.active = active
|
||||||
|
validation.rules.name = rule
|
||||||
|
validation.rules.value = value
|
||||||
|
|
||||||
|
dialog.validation.rules.title = Validation rules
|
||||||
|
dialog.validation.rules.header = Validation rules of column "{0}"
|
||||||
|
context.menu.edit.column.rules = Edit rules
|
||||||
@@ -8,14 +8,21 @@
|
|||||||
|
|
||||||
menu.open.csv = CSV Datei \u00f6ffnen
|
menu.open.csv = CSV Datei \u00f6ffnen
|
||||||
menu.open.config = Pr\u00fcfkonfiguration \u00f6ffnen
|
menu.open.config = Pr\u00fcfkonfiguration \u00f6ffnen
|
||||||
|
menu.create.config = Pr\u00fcfkonfiguration erzeugen
|
||||||
menu.save = Speichern
|
menu.save = Speichern
|
||||||
menu.save.as = Speichern als ...
|
menu.save.as = Speichern als ...
|
||||||
|
menu.save.config = Pr\u00fcfkonfiguration speichern
|
||||||
|
menu.save.as.config = Pr\u00fcfkonfiguration speichern als ...
|
||||||
|
|
||||||
menu.close = Beenden
|
menu.close = Beenden
|
||||||
menu.about = \u00dcber ...
|
menu.about = \u00dcber ...
|
||||||
menu.file = Datei
|
menu.file = Datei
|
||||||
menu.edit = Bearbeiten
|
menu.edit = Bearbeiten
|
||||||
menu.help = Hilfe
|
menu.help = Hilfe
|
||||||
menu.preferences = Einstellungen
|
menu.preferences = Einstellungen
|
||||||
|
menu.delete.row = Zeile l\u00f6schen
|
||||||
|
menu.add.row = Zeile hinzuf\u00fcgen
|
||||||
|
|
||||||
title.validation.errors = Fehler in der Datei:
|
title.validation.errors = Fehler in der Datei:
|
||||||
|
|
||||||
stateline.csv = CSV:
|
stateline.csv = CSV:
|
||||||
@@ -53,3 +60,22 @@ validation.message.regexp = entspricht nicht dem regul\u00e4ren Ausdruck {0}
|
|||||||
validation.message.header.length = Anzahl der \u00dcberschriften ist nicht korrekt! Es sind {0} aber es sollten {1} sein
|
validation.message.header.length = Anzahl der \u00dcberschriften ist nicht korrekt! Es sind {0} aber es sollten {1} sein
|
||||||
validation.message.header.match = \u00dcberschrift in Spalte {0} stimmt nicht. "{1}" sollte "{3}" sein
|
validation.message.header.match = \u00dcberschrift in Spalte {0} stimmt nicht. "{1}" sollte "{3}" sein
|
||||||
validation.message.value.of = Der Wert {0} ist nicht in der Liste {1} enthalten
|
validation.message.value.of = Der Wert {0} ist nicht in der Liste {1} enthalten
|
||||||
|
|
||||||
|
validation.rule.label.not_empty = Nicht leer:
|
||||||
|
validation.rule.label.integer = Zahl:
|
||||||
|
validation.rule.label.double = Gleitkommazahl:
|
||||||
|
validation.rule.label.minlength = Minimale L\u00e4nge:
|
||||||
|
validation.rule.label.maxlength = Maximale L\u00e4nge:
|
||||||
|
validation.rule.label.dateformat = Datumsformat:
|
||||||
|
validation.rule.label.alphanumeric = Nur Zahlen und Buchstaben:
|
||||||
|
validation.rule.label.regexp = Regul\u00e4rer Ausdruck:
|
||||||
|
validation.rule.label.value_of = In der Liste:
|
||||||
|
validation.rule.label.groovy = groovy:
|
||||||
|
validation.rules.active = Aktiv
|
||||||
|
validation.rules.name = Regel
|
||||||
|
validation.rules.value = Wert
|
||||||
|
|
||||||
|
dialog.validation.rules.title = Pr\u00fcfregeln
|
||||||
|
dialog.validation.rules.header = Pr\u00fcfregeln f\u00fcr die Spalte "{0}"
|
||||||
|
|
||||||
|
context.menu.edit.column.rules = Pr\u00fcfregeln bearbeiten
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.Spinner?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?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" />
|
||||||
|
<ColumnConstraints hgrow="NEVER" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<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 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>
|
||||||
|
<children>
|
||||||
|
<Label text="%validation.rule.label.not_empty" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<Label text="%validation.rule.label.integer" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
<Label text="%validation.rule.label.double" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
|
<Label text="%validation.rule.label.minlength" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||||
|
<Label text="%validation.rule.label.maxlength" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
|
<Label text="%validation.rule.label.dateformat" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
||||||
|
<Label text="%validation.rule.label.alphanumeric" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<Label text="%validation.rule.label.regexp" GridPane.columnIndex="1" GridPane.rowIndex="8" />
|
||||||
|
<Label text="%validation.rule.label.value_of" GridPane.columnIndex="1" GridPane.rowIndex="9" />
|
||||||
|
<Label text="%validation.rule.label.groovy" GridPane.columnIndex="1" GridPane.rowIndex="10" />
|
||||||
|
<CheckBox fx:id="notEmptyRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="1" />
|
||||||
|
<CheckBox fx:id="integerRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="2" />
|
||||||
|
<CheckBox fx:id="doublerRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="3" />
|
||||||
|
<CheckBox fx:id="alphanumericRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="4" />
|
||||||
|
<Spinner fx:id="minLengthSpinner" GridPane.columnIndex="2" GridPane.rowIndex="5" />
|
||||||
|
<Spinner fx:id="maxLengthSpinner" GridPane.columnIndex="2" GridPane.rowIndex="6" />
|
||||||
|
<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" />
|
||||||
|
<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" />
|
||||||
|
<CheckBox fx:id="enableAlphanumericRule" mnemonicParsing="false" GridPane.rowIndex="4" />
|
||||||
|
<CheckBox fx:id="enableMinLengthRule" mnemonicParsing="false" GridPane.rowIndex="5" />
|
||||||
|
<CheckBox fx:id="enableMaxLengthRule" mnemonicParsing="false" GridPane.rowIndex="6" />
|
||||||
|
<CheckBox fx:id="enableDateRule" mnemonicParsing="false" GridPane.rowIndex="7" />
|
||||||
|
<CheckBox fx:id="enableRegexpRule" mnemonicParsing="false" GridPane.rowIndex="8" />
|
||||||
|
<CheckBox fx:id="enableValueOfRule" mnemonicParsing="false" GridPane.rowIndex="9" />
|
||||||
|
<CheckBox fx:id="enableGroovyRule" mnemonicParsing="false" GridPane.rowIndex="10" />
|
||||||
|
<Label style="-fx-font-weight: bold;" text="%validation.rules.active" />
|
||||||
|
<Label style="-fx-font-weight: bold;" text="%validation.rules.name" GridPane.columnIndex="1" />
|
||||||
|
<Label style="-fx-font-weight: bold;" text="%validation.rules.value" GridPane.columnIndex="2" />
|
||||||
|
</children>
|
||||||
|
<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>
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.fx.table.model;
|
package ninja.javafx.smartcsv.fx.table.model;
|
||||||
|
|
||||||
|
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
||||||
import ninja.javafx.smartcsv.validation.Validator;
|
import ninja.javafx.smartcsv.validation.Validator;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -77,20 +78,6 @@ public class CSVModelTest {
|
|||||||
assertThat(sut.getRows().indexOf(newRow), is(newRow.getRowNumber()));
|
assertThat(sut.getRows().indexOf(newRow), is(newRow.getRowNumber()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void set_validator_calls_when_model_has_data() {
|
|
||||||
|
|
||||||
// setup
|
|
||||||
Validator validator = mock(Validator.class);
|
|
||||||
setup_model_with_one_row_one_column_and_value();
|
|
||||||
|
|
||||||
// execution
|
|
||||||
sut.setValidator(validator);
|
|
||||||
|
|
||||||
// assertion
|
|
||||||
verify(validator).isValid(TESTHEADER, TESTVALUE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// private methods
|
// private methods
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
package ninja.javafx.smartcsv.validation;
|
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
public class ConfigMock {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// constants
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private static final String HEADER_SECTION_KEY = "headers";
|
|
||||||
private static final String COLUMN_SECTION_KEY = "columns";
|
|
||||||
private static final String LIST_KEY = "list";
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// mocks
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
public static Config headerSectionConfig(String[] headerNames) {
|
|
||||||
|
|
||||||
Config headerSectionConfig = mock(Config.class);
|
|
||||||
Config listConfig = mock(Config.class);
|
|
||||||
|
|
||||||
when(headerSectionConfig.hasPath(HEADER_SECTION_KEY)).thenReturn(true);
|
|
||||||
when(headerSectionConfig.getConfig(HEADER_SECTION_KEY)).thenReturn(listConfig);
|
|
||||||
|
|
||||||
when(listConfig.hasPath(LIST_KEY)).thenReturn(true);
|
|
||||||
when(listConfig.getStringList(LIST_KEY)).thenReturn(asList(headerNames));
|
|
||||||
|
|
||||||
return headerSectionConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Config columnSectionConfig(String column, String validation, Object value) {
|
|
||||||
|
|
||||||
Config columnSectionConfig = mock(Config.class);
|
|
||||||
Config columnConfig = mock(Config.class);
|
|
||||||
Config validatorConfig = mock(Config.class);
|
|
||||||
|
|
||||||
when(columnSectionConfig.hasPath(COLUMN_SECTION_KEY)).thenReturn(true);
|
|
||||||
when(columnSectionConfig.getConfig(COLUMN_SECTION_KEY)).thenReturn(columnConfig);
|
|
||||||
|
|
||||||
when(columnConfig.hasPath(column)).thenReturn(true);
|
|
||||||
when(columnConfig.getConfig(column)).thenReturn(validatorConfig);
|
|
||||||
|
|
||||||
when(validatorConfig.hasPath(validation)).thenReturn(true);
|
|
||||||
if (value instanceof Boolean) {
|
|
||||||
when(validatorConfig.getBoolean(validation)).thenReturn((Boolean) value);
|
|
||||||
} else if (value instanceof String) {
|
|
||||||
when(validatorConfig.getString(validation)).thenReturn((String) value);
|
|
||||||
} else if (value instanceof Integer) {
|
|
||||||
when(validatorConfig.getInt(validation)).thenReturn((Integer) value);
|
|
||||||
} else if (value instanceof List) {
|
|
||||||
when(validatorConfig.getStringList(validation)).thenReturn((List<String>) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return columnSectionConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,20 +26,19 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.validation;
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static ninja.javafx.smartcsv.validation.ConfigMock.headerSectionConfig;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -52,7 +51,7 @@ public class HeaderValidationTest {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// parameters
|
// parameters
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
private Config config;
|
private ValidationConfiguration config;
|
||||||
private Boolean expectedResult;
|
private Boolean expectedResult;
|
||||||
private List<ValidationMessage> expectedErrors;
|
private List<ValidationMessage> expectedErrors;
|
||||||
private String[] headerNames;
|
private String[] headerNames;
|
||||||
@@ -66,11 +65,12 @@ public class HeaderValidationTest {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// parameterized constructor
|
// parameterized constructor
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
public HeaderValidationTest(String[] configHeaderNames,
|
public HeaderValidationTest(String configHeaderNames,
|
||||||
String[] headerNames,
|
String[] headerNames,
|
||||||
Boolean expectedResult,
|
Boolean expectedResult,
|
||||||
List<ValidationMessage> expectedErrors) {
|
List<ValidationMessage> expectedErrors) {
|
||||||
this.config = headerSectionConfig(configHeaderNames);
|
Gson gson = new GsonBuilder().create();
|
||||||
|
this.config = gson.fromJson(configHeaderNames, ValidationConfiguration.class);
|
||||||
this.headerNames = headerNames;
|
this.headerNames = headerNames;
|
||||||
this.expectedResult = expectedResult;
|
this.expectedResult = expectedResult;
|
||||||
this.expectedErrors = expectedErrors;
|
this.expectedErrors = expectedErrors;
|
||||||
@@ -106,11 +106,16 @@ public class HeaderValidationTest {
|
|||||||
@Parameterized.Parameters
|
@Parameterized.Parameters
|
||||||
public static Collection validationConfigurations() {
|
public static Collection validationConfigurations() {
|
||||||
return asList(new Object[][] {
|
return asList(new Object[][] {
|
||||||
{ new String[] {}, new String[] {}, true, null },
|
{ json(), new String[] {}, true, null },
|
||||||
{ new String[] {"a"}, new String[] {"a"}, true, null },
|
{ json("a"), new String[] {"a"}, true, null },
|
||||||
{ new String[] {"a"}, new String[] {"b"}, false, singletonList(new ValidationMessage("validation.message.header.match", "0", "a", "b"))},
|
{ json("a"), new String[] {"b"}, false, singletonList(new ValidationMessage("validation.message.header.match", "0", "a", "b"))},
|
||||||
{ new String[] {"a"}, new String[] {"a","b"}, false, singletonList(new ValidationMessage("validation.message.header.length", "2", "1"))},
|
{ json("a"), new String[] {"a","b"}, false, singletonList(new ValidationMessage("validation.message.header.length", "2", "1"))},
|
||||||
{ new String[] {"a", "b"}, new String[] {"b", "a"}, false, asList(new ValidationMessage("validation.message.header.match", "0", "a", "b"), new ValidationMessage("validation.message.header.match", "1", "b", "a")) }
|
{ json("a", "b"), new String[] {"b", "a"}, false, asList(new ValidationMessage("validation.message.header.match", "0", "a", "b"), new ValidationMessage("validation.message.header.match", "1", "b", "a")) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String json(String... headerNames) {
|
||||||
|
return "{\"headers\":{\"list\":[" + asList(headerNames).stream().collect(joining(", ")) + "]}}";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
package ninja.javafx.smartcsv.validation;
|
package ninja.javafx.smartcsv.validation;
|
||||||
|
|
||||||
import com.typesafe.config.Config;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static ninja.javafx.smartcsv.validation.ConfigMock.columnSectionConfig;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
@@ -23,7 +25,7 @@ public class ValidatorTest {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// parameters
|
// parameters
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
private Config config;
|
private ValidationConfiguration config;
|
||||||
private String column;
|
private String column;
|
||||||
private String value;
|
private String value;
|
||||||
private Boolean expectedResult;
|
private Boolean expectedResult;
|
||||||
@@ -39,14 +41,14 @@ public class ValidatorTest {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// parameterized constructor
|
// parameterized constructor
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
public ValidatorTest(String configcolumn,
|
public ValidatorTest(String config,
|
||||||
String configValidation,
|
|
||||||
Object configValue,
|
|
||||||
String column,
|
String column,
|
||||||
String value,
|
String value,
|
||||||
Boolean expectedResult,
|
Boolean expectedResult,
|
||||||
ValidationMessage expectedError) {
|
ValidationMessage expectedError) {
|
||||||
this.config = columnSectionConfig(configcolumn, configValidation, configValue);
|
System.out.println(config);
|
||||||
|
Gson gson = new GsonBuilder().create();
|
||||||
|
this.config = gson.fromJson(config, ValidationConfiguration.class);
|
||||||
this.column = column;
|
this.column = column;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.expectedResult = expectedResult;
|
this.expectedResult = expectedResult;
|
||||||
@@ -84,30 +86,42 @@ public class ValidatorTest {
|
|||||||
@Parameterized.Parameters
|
@Parameterized.Parameters
|
||||||
public static Collection validationConfigurations() {
|
public static Collection validationConfigurations() {
|
||||||
return asList(new Object[][] {
|
return asList(new Object[][] {
|
||||||
{ "column", "not empty", true, "column", "value", true, null },
|
{ json("column", "not empty", true), "column", "value", true, null },
|
||||||
{ "column", "not empty", true, "column", "", false, new ValidationMessage("validation.message.not.empty") },
|
{ json("column", "not empty", true), "column", "", false, new ValidationMessage("validation.message.not.empty") },
|
||||||
{ "column", "not empty", true, "column", null, false, new ValidationMessage("validation.message.not.empty") },
|
{ json("column", "not empty", true), "column", null, false, new ValidationMessage("validation.message.not.empty") },
|
||||||
{ "column", "integer", true, "column", "999", true, null },
|
{ json("column", "integer", true), "column", "999", true, null },
|
||||||
{ "column", "integer", true, "column", "a", false, new ValidationMessage("validation.message.integer") },
|
{ json("column", "integer", true), "column", "a", false, new ValidationMessage("validation.message.integer") },
|
||||||
{ "column", "double", true, "column", "999", true, null },
|
{ json("column", "double", true), "column", "999", true, null },
|
||||||
{ "column", "double", true, "column", "999.000", true, null },
|
{ json("column", "double", true), "column", "999.000", true, null },
|
||||||
{ "column", "double", true, "column", "a", false, new ValidationMessage("validation.message.double") },
|
{ json("column", "double", true), "column", "a", false, new ValidationMessage("validation.message.double") },
|
||||||
{ "column", "minlength", 2, "column", "12", true, null },
|
{ json("column", "minlength", 2), "column", "12", true, null },
|
||||||
{ "column", "minlength", 2, "column", "1", false, new ValidationMessage("validation.message.min.length", "2") },
|
{ json("column", "minlength", 2), "column", "1", false, new ValidationMessage("validation.message.min.length", "2") },
|
||||||
{ "column", "maxlength", 2, "column", "12", true, null },
|
{ json("column", "maxlength", 2), "column", "12", true, null },
|
||||||
{ "column", "maxlength", 2, "column", "123", false, new ValidationMessage("validation.message.max.length", "2") },
|
{ json("column", "maxlength", 2), "column", "123", false, new ValidationMessage("validation.message.max.length", "2") },
|
||||||
{ "column", "date", "yyyyMMdd", "column", "20151127", true, null },
|
{ json("column", "date", "yyyyMMdd"), "column", "20151127", true, null },
|
||||||
{ "column", "date", "yyyyMMdd", "column", "27.11.2015", false, new ValidationMessage("validation.message.date.format", "yyyyMMdd") },
|
{ json("column", "date", "yyyyMMdd"), "column", "27.11.2015", false, new ValidationMessage("validation.message.date.format", "yyyyMMdd") },
|
||||||
{ "column", "alphanumeric", true, "column", "abcABC123", true, null },
|
{ json("column", "alphanumeric", true), "column", "abcABC123", true, null },
|
||||||
{ "column", "alphanumeric", true, "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") },
|
{ json("column", "alphanumeric", true), "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") },
|
||||||
{ "column", "regexp", "[a-z]*", "column", "abc", true, null },
|
{ json("column", "regexp", "[a-z]*"), "column", "abc", true, null },
|
||||||
{ "column", "regexp", "[a-z]*", "column", "abcA", false, new ValidationMessage("validation.message.regexp", "[a-z]*") },
|
{ json("column", "regexp", "[a-z]*"), "column", "abcA", false, new ValidationMessage("validation.message.regexp", "[a-z]*") },
|
||||||
{ "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "abcdef", true, null },
|
{ json("column", "groovy", "value.contains('a')? 'true' : 'no a inside'"), "column", "abcdef", true, null },
|
||||||
{ "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, new ValidationMessage("no a inside") },
|
{ json("column", "groovy", "value.contains('a')? 'true' : 'no a inside'"), "column", "bcdefg", false, new ValidationMessage("no a inside") },
|
||||||
{ "column", "value of", asList("a","b","c","d","e"), "column", "c", true, null },
|
{ json("column", "value of", asList("a","b","c","d","e")), "column", "c", true, null },
|
||||||
{ "column", "value of", asList("a","b","c","d","e"), "column", "f", false, new ValidationMessage("validation.message.value.of", "f", "a, b, c, d, e") },
|
{ json("column", "value of", asList("a","b","c","d","e")), "column", "f", false, new ValidationMessage("validation.message.value.of", "f", "a, b, c, d, e") },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String json(String column, String rule, Object value) {
|
||||||
|
String json = "{\"columns\":{\"" + column + "\":{\"" + rule + "\":";
|
||||||
|
if (value instanceof String) {
|
||||||
|
json += "\""+ value + "\"";
|
||||||
|
} else if (value instanceof List) {
|
||||||
|
List<String> list = (List<String>)value;
|
||||||
|
json += "[" + list.stream().collect(joining(", ")) + "]";
|
||||||
|
} else {
|
||||||
|
json += value;
|
||||||
|
}
|
||||||
|
json += "}}}";
|
||||||
|
return json;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user