mirror of
https://github.com/frosch95/SmartCSV.fx.git
synced 2026-04-11 21:48:22 +02:00
Compare commits
55 Commits
version_0_
...
0.7
| Author | SHA1 | Date | |
|---|---|---|---|
| e6742a4e30 | |||
| 7472c66823 | |||
| c7873ef0ba | |||
| b79a20201e | |||
|
|
3ebf78805c | ||
| dcdea4236f | |||
| 11358c5bec | |||
| 9c21357059 | |||
| 909617d9b7 | |||
| ab952aa98f | |||
| a7bd8d5a3e | |||
| bc59f08bbb | |||
| 4c4d25f3b5 | |||
| 6af20575e8 | |||
| b6731f7641 | |||
| d2f81d7d3e | |||
| 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 |
12
README.md
12
README.md
@@ -7,7 +7,17 @@ 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.
|
||||
|
||||
##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.6)](https://github.com/frosch95/SmartCSV.fx/releases/download/0.6/SmartCSV.fx-0.6-SNAPSHOT.zip)
|
||||
|
||||
##Talks
|
||||
[Introduction](http://javafx.ninja/talks/introduction/)
|
||||
|
||||
##License
|
||||
###The MIT License (MIT)
|
||||
|
||||
23
build.gradle
23
build.gradle
@@ -1,5 +1,5 @@
|
||||
group 'ninja.javafx'
|
||||
version '0.3-SNAPSHOT'
|
||||
version '0.7-SNAPSHOT'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'groovy'
|
||||
@@ -18,10 +18,21 @@ dependencies {
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'
|
||||
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.springframework', name:'spring-context', version: '4.2.4.RELEASE'
|
||||
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.7'
|
||||
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: 'com.typesafe', name: 'config', version: '1.3.0'
|
||||
compile group: 'commons-validator', name: 'commons-validator', version: '1.5.0'
|
||||
compile group: 'de.jensd', name: 'fontawesomefx', version: '8.8'
|
||||
compile group: 'commons-validator', name: 'commons-validator', version: '1.5.1'
|
||||
compile group: 'de.jensd', name: 'fontawesomefx-commons', version: '8.12'
|
||||
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
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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
|
||||
|
||||
# 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
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
@@ -114,6 +109,7 @@ fi
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
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
|
||||
|
||||
: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 "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
rootProject.name = 'SmartCSV.ninja.javafx.smartcsv.fx'
|
||||
rootProject.name = 'SmartCSV.fx'
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* read some file
|
||||
*/
|
||||
public interface FileReader {
|
||||
public interface FileReader<E> {
|
||||
E getContent();
|
||||
void read(File filename) throws IOException;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* write some file
|
||||
*/
|
||||
public interface FileWriter {
|
||||
public interface FileWriter<E> {
|
||||
void setContent(E content);
|
||||
void write(File filename) throws IOException;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ package ninja.javafx.smartcsv.csv;
|
||||
import ninja.javafx.smartcsv.FileReader;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.supercsv.io.CsvMapReader;
|
||||
import org.supercsv.io.ICsvMapReader;
|
||||
|
||||
@@ -40,8 +39,7 @@ import java.util.Map;
|
||||
/**
|
||||
* reads the csv file and stores the values in csv model
|
||||
*/
|
||||
@Service
|
||||
public class CSVFileReader extends CSVConfigurable implements FileReader {
|
||||
public class CSVFileReader extends CSVConfigurable implements FileReader<CSVModel> {
|
||||
|
||||
private CSVModel model;
|
||||
|
||||
@@ -61,7 +59,7 @@ public class CSVFileReader extends CSVConfigurable implements FileReader {
|
||||
while ((customerMap = mapReader.read(header)) != null) {
|
||||
CSVRow row = model.addRow();
|
||||
for (String column : header) {
|
||||
row.addValue(column, customerMap.get(column));
|
||||
model.addValue(row, column, customerMap.get(column));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +70,7 @@ public class CSVFileReader extends CSVConfigurable implements FileReader {
|
||||
}
|
||||
}
|
||||
|
||||
public CSVModel getData() {
|
||||
public CSVModel getContent() {
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ package ninja.javafx.smartcsv.csv;
|
||||
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.supercsv.io.CsvMapWriter;
|
||||
import org.supercsv.io.ICsvMapWriter;
|
||||
|
||||
@@ -42,12 +41,11 @@ import static java.util.stream.Collectors.toMap;
|
||||
/**
|
||||
* filewriter for the csv
|
||||
*/
|
||||
@Service
|
||||
public class CSVFileWriter extends CSVConfigurable implements ninja.javafx.smartcsv.FileWriter {
|
||||
public class CSVFileWriter extends CSVConfigurable implements ninja.javafx.smartcsv.FileWriter<CSVModel> {
|
||||
|
||||
private CSVModel model;
|
||||
|
||||
public void setModel(CSVModel model) {
|
||||
public void setContent(CSVModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
|
||||
71
src/main/java/ninja/javafx/smartcsv/export/ErrorExport.java
Normal file
71
src/main/java/ninja/javafx/smartcsv/export/ErrorExport.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package ninja.javafx.smartcsv.export;
|
||||
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||
|
||||
/**
|
||||
* this class exports the error messages into a log file
|
||||
*/
|
||||
@org.springframework.stereotype.Service
|
||||
public class ErrorExport extends Service {
|
||||
|
||||
private CSVModel model;
|
||||
private File file;
|
||||
private ResourceBundle resourceBundle;
|
||||
private String csvFilename;
|
||||
|
||||
public void setCsvFilename(String csvFilename) {
|
||||
this.csvFilename = csvFilename;
|
||||
}
|
||||
|
||||
public void setResourceBundle(ResourceBundle resourceBundle) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
}
|
||||
|
||||
public void setModel(CSVModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public void setFile(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task createTask() {
|
||||
return new Task() {
|
||||
@Override
|
||||
protected Void call() throws Exception {
|
||||
try {
|
||||
StringWriter log = new StringWriter();
|
||||
log.append(
|
||||
format(resourceBundle.getString("log.header.message"),
|
||||
csvFilename,
|
||||
Integer.toString(model.getValidationError().size()))).append("\n\n");
|
||||
for (ValidationError error:model.getValidationError()) {
|
||||
log.append(
|
||||
format(resourceBundle.getString("log.message"),
|
||||
error.getLineNumber().toString(),
|
||||
error.getColumn(),
|
||||
getI18nValidatioMessage(resourceBundle, error))).append("\n");
|
||||
}
|
||||
Files.write(file.toPath(), log.toString().getBytes());
|
||||
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,9 @@ package ninja.javafx.smartcsv.fx;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import ninja.javafx.smartcsv.fx.about.AboutController;
|
||||
import org.springframework.context.annotation.*;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
|
||||
package ninja.javafx.smartcsv.fx;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.WeakListChangeListener;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -36,12 +38,13 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
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.CSVFileWriter;
|
||||
import ninja.javafx.smartcsv.export.ErrorExport;
|
||||
import ninja.javafx.smartcsv.files.FileStorage;
|
||||
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.list.GotoLineDialog;
|
||||
import ninja.javafx.smartcsv.fx.preferences.PreferencesController;
|
||||
import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory;
|
||||
import ninja.javafx.smartcsv.fx.table.ValidationCellFactory;
|
||||
@@ -50,10 +53,13 @@ import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
||||
import ninja.javafx.smartcsv.fx.util.LoadFileService;
|
||||
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.PreferencesFileWriter;
|
||||
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
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.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -66,8 +72,10 @@ import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.text.MessageFormat.format;
|
||||
import static javafx.application.Platform.exit;
|
||||
import static javafx.application.Platform.runLater;
|
||||
import static javafx.beans.binding.Bindings.*;
|
||||
import static javafx.scene.layout.AnchorPane.*;
|
||||
|
||||
/**
|
||||
@@ -85,37 +93,35 @@ public class SmartCSVController extends FXMLController {
|
||||
".SmartCSV.fx" +
|
||||
File.separator + "" +
|
||||
"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";
|
||||
public static final String EXPORT_LOG_FILTER_TEXT = "Error log files (*.log)";
|
||||
public static final String EXPORT_LOG_FILTER_EXTENSION = "*.log";
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// injections
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Autowired
|
||||
private PreferencesFileReader preferencesLoader;
|
||||
|
||||
@Autowired
|
||||
private PreferencesFileWriter preferencesWriter;
|
||||
|
||||
@Autowired
|
||||
private CSVFileReader csvLoader;
|
||||
|
||||
@Autowired
|
||||
private ValidationFileReader validationLoader;
|
||||
|
||||
@Autowired
|
||||
private CSVFileWriter csvFileWriter;
|
||||
|
||||
@Autowired
|
||||
private AboutController aboutController;
|
||||
|
||||
@Autowired
|
||||
private PreferencesController preferencesController;
|
||||
|
||||
@Autowired
|
||||
private ValidationEditorController validationEditorController;
|
||||
|
||||
@Autowired
|
||||
private LoadFileService loadFileService;
|
||||
|
||||
@Autowired
|
||||
private SaveFileService saveFileService;;
|
||||
private SaveFileService saveFileService;
|
||||
|
||||
@Autowired
|
||||
private ErrorExport errorExport;
|
||||
|
||||
@FXML
|
||||
private BorderPane applicationPane;
|
||||
@@ -129,9 +135,6 @@ public class SmartCSVController extends FXMLController {
|
||||
@FXML
|
||||
private Label stateName;
|
||||
|
||||
@FXML
|
||||
private ListView errorList;
|
||||
|
||||
@FXML
|
||||
private AnchorPane tableWrapper;
|
||||
|
||||
@@ -141,6 +144,59 @@ public class SmartCSVController extends FXMLController {
|
||||
@FXML
|
||||
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 MenuItem gotoLineMenuItem;
|
||||
|
||||
@FXML
|
||||
private MenuItem exportMenuItem;
|
||||
|
||||
@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;
|
||||
|
||||
@FXML
|
||||
private Button exportButton;
|
||||
|
||||
@FXML
|
||||
private Label currentLineNumber;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// members
|
||||
@@ -148,13 +204,16 @@ public class SmartCSVController extends FXMLController {
|
||||
|
||||
private ValidationCellFactory cellFactory;
|
||||
|
||||
private CSVModel model;
|
||||
private TableView<CSVRow> tableView;
|
||||
private BooleanProperty fileChanged = new SimpleBooleanProperty(true);
|
||||
private ErrorSideBar errorSideBar;
|
||||
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
|
||||
@@ -163,16 +222,35 @@ public class SmartCSVController extends FXMLController {
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resourceBundle) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
saveFileService.setWriter(csvFileWriter);
|
||||
cellFactory = new ValidationCellFactory(resourceBundle);
|
||||
errorList.setCellFactory(param -> new ValidationErrorListCell(resourceBundle));
|
||||
errorList.getSelectionModel().selectedItemProperty().addListener(observable -> scrollToError());
|
||||
fileChanged.addListener(observable -> setStateName());
|
||||
setStateName();
|
||||
loadCsvPreferences();
|
||||
|
||||
setupTableCellFactory();
|
||||
setupErrorSideBar(resourceBundle);
|
||||
|
||||
bindMenuItemsToContentExistence(currentCsvFile, saveMenuItem, saveAsMenuItem, addRowMenuItem, gotoLineMenuItem, createConfigMenuItem, loadConfigMenuItem);
|
||||
bindButtonsToContentExistence(currentCsvFile, saveButton, saveAsButton, addRowButton, createConfigButton, loadConfigButton);
|
||||
|
||||
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 +270,44 @@ public class SmartCSVController extends FXMLController {
|
||||
|
||||
@FXML
|
||||
public void openCsv(ActionEvent actionEvent) {
|
||||
currentCsvFile = loadFile(csvLoader,
|
||||
"CSV files (*.csv)",
|
||||
"*.csv",
|
||||
"Open CSV",
|
||||
currentCsvFile);
|
||||
enableSaveMenuItems();
|
||||
setCsvFileName();
|
||||
loadFile(CSV_FILTER_TEXT, CSV_FILTER_EXTENSION, "Open CSV", currentCsvFile);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void openConfig(ActionEvent actionEvent) {
|
||||
currentConfigFile = loadFile(validationLoader,
|
||||
"JSON files (*.json)",
|
||||
"*.json",
|
||||
"Open Validation Configuration",
|
||||
currentConfigFile);
|
||||
setConfigFileName();
|
||||
loadFile(JSON_FILTER_TEXT, JSON_FILTER_EXTENSION, "Open Validation Configuration", currentConfigFile);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void createConfig(ActionEvent actionEvent) {
|
||||
currentConfigFile.setContent(currentCsvFile.getContent().createValidationConfiguration());
|
||||
currentConfigFile.setFile(null);
|
||||
currentConfigFile.setFileChanged(true);
|
||||
resetContent();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void saveCsv(ActionEvent actionEvent) {
|
||||
csvFileWriter.setModel(model);
|
||||
useSaveFileService(csvFileWriter, currentCsvFile);
|
||||
useSaveFileService(currentCsvFile);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void saveAsCsv(ActionEvent actionEvent) {
|
||||
csvFileWriter.setModel(model);
|
||||
currentCsvFile = saveFile(csvFileWriter,
|
||||
"CSV files (*.csv)",
|
||||
"*.csv",
|
||||
currentCsvFile);
|
||||
setCsvFileName();
|
||||
saveFile(CSV_FILTER_TEXT, CSV_FILTER_EXTENSION, currentCsvFile);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void saveConfig(ActionEvent actionEvent) {
|
||||
if (currentConfigFile.getFile() == null) {
|
||||
saveAsConfig(actionEvent);
|
||||
} else {
|
||||
useSaveFileService(currentConfigFile);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void saveAsConfig(ActionEvent actionEvent) {
|
||||
saveFile(JSON_FILTER_TEXT, JSON_FILTER_EXTENSION, currentConfigFile);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -261,13 +344,69 @@ public class SmartCSVController extends FXMLController {
|
||||
setCsvPreference(csvPreference);
|
||||
saveCsvPreferences(csvPreference);
|
||||
} 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()) {
|
||||
currentCsvFile.getContent().addValue(row, column, "");
|
||||
}
|
||||
currentCsvFile.setFileChanged(true);
|
||||
resetContent();
|
||||
|
||||
selectNewRow();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void gotoLine(ActionEvent actionEvent) {
|
||||
int maxLineNumber = currentCsvFile.getContent().getRows().size();
|
||||
GotoLineDialog dialog = new GotoLineDialog(maxLineNumber);
|
||||
dialog.setTitle(resourceBundle.getString("dialog.goto.line.title"));
|
||||
dialog.setHeaderText(format(resourceBundle.getString("dialog.goto.line.header.text"), maxLineNumber));
|
||||
dialog.setContentText(resourceBundle.getString("dialog.goto.line.label"));
|
||||
Optional<Integer> result = dialog.showAndWait();
|
||||
if (result.isPresent()){
|
||||
Integer lineNumber = result.get();
|
||||
if (lineNumber != null) {
|
||||
tableView.scrollTo(max(0, lineNumber - 2));
|
||||
tableView.getSelectionModel().select(lineNumber - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void export(ActionEvent actionEvent) {
|
||||
final FileChooser fileChooser = new FileChooser();
|
||||
|
||||
//Set extension filter
|
||||
final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(EXPORT_LOG_FILTER_TEXT, EXPORT_LOG_FILTER_EXTENSION);
|
||||
fileChooser.getExtensionFilters().add(extFilter);
|
||||
fileChooser.setTitle("Save");
|
||||
|
||||
//Show open file dialog
|
||||
File file = fileChooser.showSaveDialog(applicationPane.getScene().getWindow());
|
||||
if (file != null) {
|
||||
errorExport.setCsvFilename(currentCsvFile.getFile().getName());
|
||||
errorExport.setModel(currentCsvFile.getContent());
|
||||
errorExport.setFile(file);
|
||||
errorExport.setResourceBundle(resourceBundle);
|
||||
errorExport.restart();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canExit() {
|
||||
boolean canExit = true;
|
||||
if (model != null && fileChanged.get()) {
|
||||
if (currentCsvFile.getContent() != null && currentCsvFile.isFileChanged()) {
|
||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
alert.setTitle(resourceBundle.getString("dialog.exit.title"));
|
||||
alert.setHeaderText(resourceBundle.getString("dialog.exit.header.text"));
|
||||
@@ -282,74 +421,111 @@ public class SmartCSVController extends FXMLController {
|
||||
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(column);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// private methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void enableSaveMenuItems() {
|
||||
if (currentCsvFile != null) {
|
||||
saveMenuItem.setDisable(false);
|
||||
saveAsMenuItem.setDisable(false);
|
||||
private void selectNewRow() {
|
||||
int lastRow = tableView.getItems().size()-1;
|
||||
tableView.scrollTo(lastRow);
|
||||
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() {
|
||||
if (currentCsvFile != null) {
|
||||
csvName.setText(currentCsvFile.getName());
|
||||
private void bindButtonsToContentExistence(FileStorage file, Button... items) {
|
||||
for (Button item: items) {
|
||||
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 bindLineNumber() {
|
||||
currentLineNumber.textProperty().bind(tableView.getSelectionModel().selectedIndexProperty().add(1).asString());
|
||||
}
|
||||
|
||||
private void loadCsvPreferencesFromFile() {
|
||||
if (csvPreferenceFile.getFile().exists()) {
|
||||
useLoadFileService(csvPreferenceFile, event -> setCsvPreference(csvPreferenceFile.getContent()));
|
||||
} 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) {
|
||||
try {
|
||||
createPreferenceFile();
|
||||
preferencesWriter.setCsvPreference(csvPreference);
|
||||
useSaveFileService(preferencesWriter, PREFERENCES_FILE);
|
||||
csvPreferenceFile.setContent(csvPreference);
|
||||
useSaveFileService(csvPreferenceFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void createPreferenceFile() throws IOException {
|
||||
if (!PREFERENCES_FILE.exists()) {
|
||||
if (!csvPreferenceFile.getFile().exists()) {
|
||||
createPreferencesFileFolder();
|
||||
PREFERENCES_FILE.createNewFile();
|
||||
csvPreferenceFile.getFile().createNewFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void createPreferencesFileFolder() {
|
||||
if (!PREFERENCES_FILE.getParentFile().exists()) {
|
||||
PREFERENCES_FILE.getParentFile().mkdir();
|
||||
if (!csvPreferenceFile.getFile().getParentFile().exists()) {
|
||||
csvPreferenceFile.getFile().getParentFile().mkdir();
|
||||
}
|
||||
}
|
||||
|
||||
private void setCsvPreference(CsvPreference csvPreference) {
|
||||
csvLoader.setCsvPreference(csvPreference);
|
||||
csvFileWriter.setCsvPreference(csvPreference);
|
||||
preferencesController.setCsvPreference(csvPreference);
|
||||
}
|
||||
|
||||
private File loadFile(FileReader fileReader,
|
||||
String filterText,
|
||||
private void loadFile(String filterText,
|
||||
String filter,
|
||||
String title,
|
||||
File initChildFile) {
|
||||
FileStorage storageFile) {
|
||||
final FileChooser fileChooser = new FileChooser();
|
||||
|
||||
//Set extension filter
|
||||
@@ -357,77 +533,76 @@ public class SmartCSVController extends FXMLController {
|
||||
fileChooser.getExtensionFilters().add(extFilter);
|
||||
fileChooser.setTitle(title);
|
||||
|
||||
if (initChildFile != null) {
|
||||
fileChooser.setInitialDirectory(initChildFile.getParentFile());
|
||||
if (storageFile.getFile() != null) {
|
||||
fileChooser.setInitialDirectory(storageFile.getFile().getParentFile());
|
||||
}
|
||||
|
||||
//Show open file dialog
|
||||
File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow());
|
||||
if (file != null) {
|
||||
useLoadFileService(fileReader, file);
|
||||
return file;
|
||||
} else {
|
||||
return initChildFile;
|
||||
storageFile.setFile(file);
|
||||
useLoadFileService(storageFile, t -> resetContent());
|
||||
}
|
||||
}
|
||||
|
||||
private File saveFile(FileWriter writer, String filterText, String filter, File initFile) {
|
||||
File file = initFile;
|
||||
if (model != null) {
|
||||
private File saveFile(String filterText, String filter, FileStorage fileStorage) {
|
||||
File file = fileStorage.getFile();
|
||||
if (fileStorage.getContent() != null) {
|
||||
final FileChooser fileChooser = new FileChooser();
|
||||
|
||||
//Set extension filter
|
||||
final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(filterText, filter);
|
||||
fileChooser.getExtensionFilters().add(extFilter);
|
||||
|
||||
if (initFile != null) {
|
||||
fileChooser.setInitialDirectory(initFile.getParentFile());
|
||||
fileChooser.setInitialFileName(initFile.getName());
|
||||
if (fileStorage.getFile() != null) {
|
||||
fileChooser.setInitialDirectory(fileStorage.getFile().getParentFile());
|
||||
fileChooser.setInitialFileName(fileStorage.getFile().getName());
|
||||
}
|
||||
fileChooser.setTitle("Save File");
|
||||
|
||||
//Show open file dialog
|
||||
file = fileChooser.showSaveDialog(applicationPane.getScene().getWindow());
|
||||
if (file != null) {
|
||||
useSaveFileService(writer, file);
|
||||
fileStorage.setFile(file);
|
||||
useSaveFileService(fileStorage);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private void useLoadFileService(FileReader fileReader, File file) {
|
||||
loadFileService.setFile(file);
|
||||
loadFileService.setFileReader(fileReader);
|
||||
private void useLoadFileService(FileStorage fileStorage, EventHandler<WorkerStateEvent> onSucceededHandler) {
|
||||
loadFileService.setFileStorage(fileStorage);
|
||||
loadFileService.restart();
|
||||
loadFileService.setOnSucceeded(event -> runLater(() -> {
|
||||
resetContent();
|
||||
fileChanged.setValue(false);
|
||||
}));
|
||||
loadFileService.setOnSucceeded(onSucceededHandler);
|
||||
}
|
||||
|
||||
private void useSaveFileService(FileWriter writer, File file) {
|
||||
saveFileService.setFile(file);
|
||||
saveFileService.setWriter(writer);
|
||||
private void useSaveFileService(FileStorage fileStorage) {
|
||||
saveFileService.setFileStorage(fileStorage);
|
||||
saveFileService.restart();
|
||||
saveFileService.setOnSucceeded(event -> runLater(() -> {
|
||||
resetContent();
|
||||
fileChanged.setValue(false);
|
||||
}));
|
||||
saveFileService.setOnSucceeded(t -> resetContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new table view and add the new content
|
||||
*/
|
||||
private void resetContent() {
|
||||
model = csvLoader.getData();
|
||||
if (model != null) {
|
||||
model.setValidator(validationLoader.getValidator());
|
||||
tableView = new TableView<>();
|
||||
resetExportButtons();
|
||||
|
||||
for (String column : model.getHeader()) {
|
||||
if (currentCsvFile.getContent() != null) {
|
||||
currentCsvFile.getContent().getValidationError().addListener(weakErrorListListener);
|
||||
currentCsvFile.getContent().setValidationConfiguration(currentConfigFile.getContent());
|
||||
validationEditorController.setValidationConfiguration(currentConfigFile.getContent());
|
||||
tableView = new TableView<>();
|
||||
bindLineNumber();
|
||||
|
||||
bindMenuItemsToTableSelection(deleteRowMenuItem);
|
||||
bindButtonsToTableSelection(deleteRowButton);
|
||||
|
||||
for (String column : currentCsvFile.getContent().getHeader()) {
|
||||
addColumn(column, tableView);
|
||||
}
|
||||
tableView.getItems().setAll(model.getRows());
|
||||
|
||||
tableView.getItems().setAll(currentCsvFile.getContent().getRows());
|
||||
tableView.setEditable(true);
|
||||
|
||||
setBottomAnchor(tableView, 0.0);
|
||||
@@ -435,29 +610,45 @@ public class SmartCSVController extends FXMLController {
|
||||
setLeftAnchor(tableView, 0.0);
|
||||
setRightAnchor(tableView, 0.0);
|
||||
tableWrapper.getChildren().setAll(tableView);
|
||||
|
||||
errorList.setItems(model.getValidationError());
|
||||
errorSideBar.setModel(currentCsvFile.getContent());
|
||||
binExportButtons();
|
||||
}
|
||||
}
|
||||
|
||||
private void binExportButtons() {
|
||||
exportButton.disableProperty().bind(Bindings.isEmpty(currentCsvFile.getContent().getValidationError()));
|
||||
exportMenuItem.disableProperty().bind(Bindings.isEmpty(currentCsvFile.getContent().getValidationError()));
|
||||
}
|
||||
|
||||
private void resetExportButtons() {
|
||||
exportButton.disableProperty().unbind();
|
||||
exportMenuItem.disableProperty().unbind();
|
||||
exportButton.disableProperty().setValue(true);
|
||||
exportMenuItem.disableProperty().setValue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column with the given name to the tableview
|
||||
* @param header name of the column header
|
||||
* @param tableView the tableview
|
||||
*/
|
||||
private void addColumn(String header, TableView tableView) {
|
||||
private void addColumn(final String header, TableView tableView) {
|
||||
TableColumn column = new TableColumn(header);
|
||||
column.setCellValueFactory(new ObservableMapValueFactory(header));
|
||||
column.setCellFactory(cellFactory);
|
||||
column.setEditable(true);
|
||||
column.setSortable(false);
|
||||
|
||||
ContextMenu contextMenu = contextMenuForColumn(header);
|
||||
column.setContextMenu(contextMenu);
|
||||
|
||||
column.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<CSVRow, CSVValue>>() {
|
||||
@Override
|
||||
public void handle(TableColumn.CellEditEvent<CSVRow, CSVValue> event) {
|
||||
event.getTableView().getItems().get(event.getTablePosition().getRow()).
|
||||
getColumns().get(header).setValue(event.getNewValue());
|
||||
runLater(() -> {
|
||||
fileChanged.setValue(true);
|
||||
model.revalidate();
|
||||
currentCsvFile.setFileChanged(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -465,8 +656,16 @@ public class SmartCSVController extends FXMLController {
|
||||
tableView.getColumns().add(column);
|
||||
}
|
||||
|
||||
private void scrollToError() {
|
||||
ValidationError entry = (ValidationError)errorList.getSelectionModel().getSelectedItem();
|
||||
private ContextMenu contextMenuForColumn(String header) {
|
||||
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.getLineNumber() != null) {
|
||||
tableView.scrollTo(max(0, entry.getLineNumber() - 1));
|
||||
@@ -476,16 +675,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("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
175
src/main/java/ninja/javafx/smartcsv/fx/list/ErrorSideBar.java
Normal file
175
src/main/java/ninja/javafx/smartcsv/fx/list/ErrorSideBar.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
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.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.getI18nValidatioMessageWithColumn;
|
||||
|
||||
/**
|
||||
* 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(getI18nValidatioMessageWithColumn(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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package ninja.javafx.smartcsv.fx.list;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Created by abi on 05.08.2016.
|
||||
*/
|
||||
public class GotoLineDialog extends Dialog<Integer> {
|
||||
|
||||
private final GridPane grid;
|
||||
private final Label label;
|
||||
private final TextField textField;
|
||||
|
||||
public GotoLineDialog(int maxLineNumber) {
|
||||
final DialogPane dialogPane = getDialogPane();
|
||||
|
||||
this.textField = new TextField("");
|
||||
this.textField.setMaxWidth(Double.MAX_VALUE);
|
||||
|
||||
UnaryOperator<TextFormatter.Change> filter = change -> {
|
||||
String text = change.getText();
|
||||
|
||||
if (text.matches("[0-9]*")) {
|
||||
try {
|
||||
int lineNumber = Integer.parseInt(textField.getText() + text);
|
||||
if (lineNumber <= maxLineNumber) {
|
||||
return change;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// this happens when focusing textfield or press special keys like DEL
|
||||
return change;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
|
||||
textField.setTextFormatter(textFormatter);
|
||||
|
||||
GridPane.setHgrow(textField, Priority.ALWAYS);
|
||||
GridPane.setFillWidth(textField, true);
|
||||
|
||||
label = createContentLabel(dialogPane.getContentText());
|
||||
label.setPrefWidth(Region.USE_COMPUTED_SIZE);
|
||||
label.textProperty().bind(dialogPane.contentTextProperty());
|
||||
|
||||
this.grid = new GridPane();
|
||||
this.grid.setHgap(10);
|
||||
this.grid.setMaxWidth(Double.MAX_VALUE);
|
||||
this.grid.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
dialogPane.contentTextProperty().addListener(o -> updateGrid());
|
||||
|
||||
dialogPane.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
|
||||
|
||||
updateGrid();
|
||||
|
||||
setResultConverter((dialogButton) -> {
|
||||
ButtonBar.ButtonData data = dialogButton == null ? null : dialogButton.getButtonData();
|
||||
return data == ButtonBar.ButtonData.OK_DONE ? Integer.parseInt(textField.getText()) : null;
|
||||
});
|
||||
}
|
||||
|
||||
private Label createContentLabel(String text) {
|
||||
Label label = new Label(text);
|
||||
label.setMaxWidth(Double.MAX_VALUE);
|
||||
label.setMaxHeight(Double.MAX_VALUE);
|
||||
label.getStyleClass().add("content");
|
||||
label.setWrapText(true);
|
||||
label.setPrefWidth(360);
|
||||
return label;
|
||||
}
|
||||
|
||||
private void updateGrid() {
|
||||
grid.getChildren().clear();
|
||||
|
||||
grid.add(label, 0, 0);
|
||||
grid.add(textField, 1, 0);
|
||||
getDialogPane().setContent(grid);
|
||||
|
||||
Platform.runLater(() -> textField.requestFocus());
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,8 @@
|
||||
|
||||
package ninja.javafx.smartcsv.fx.preferences;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
|
||||
@@ -37,6 +37,7 @@ import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -77,7 +78,7 @@ public class EditableValidationCell extends TableCell<CSVRow, CSVValue> {
|
||||
setStyle("");
|
||||
setTooltip(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())));
|
||||
}
|
||||
|
||||
|
||||
@@ -28,31 +28,40 @@ package ninja.javafx.smartcsv.fx.table.model;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import ninja.javafx.smartcsv.validation.RevalidationService;
|
||||
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
import ninja.javafx.smartcsv.validation.Validator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* The CSVModel is the client representation for the csv filepath.
|
||||
* It holds the data in rows, stores the header and manages the validator.
|
||||
*/
|
||||
public class CSVModel {
|
||||
public final class CSVModel implements ColumnValueProvider {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(CSVModel.class);
|
||||
|
||||
private Validator validator;
|
||||
private ObservableList<CSVRow> rows = FXCollections.observableArrayList();
|
||||
private String[] header;
|
||||
private ObservableList<ValidationError> validationError = FXCollections.observableArrayList();
|
||||
private RevalidationService revalidationService = new RevalidationService();
|
||||
|
||||
/**
|
||||
* sets the validator for the data revalidates
|
||||
* @param validator the validator for this data
|
||||
* sets the validator configuration for the data revalidates
|
||||
*
|
||||
* @param validationConfiguration the validator configuration for this data
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
public void setValidationConfiguration(ValidationConfiguration validationConfiguration) {
|
||||
this.validator = new Validator(validationConfiguration, this);
|
||||
revalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the data as a list of rows of the
|
||||
*
|
||||
* @return list of rows
|
||||
*/
|
||||
public ObservableList<CSVRow> getRows() {
|
||||
@@ -65,18 +74,30 @@ public class CSVModel {
|
||||
|
||||
/**
|
||||
* adds a new and empty row
|
||||
*
|
||||
* @return the new row
|
||||
*/
|
||||
public CSVRow addRow() {
|
||||
CSVRow row = new CSVRow();
|
||||
row.setValidator(validator);
|
||||
row.setRowNumber(rows.size());
|
||||
rows.add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
public void addValue(final CSVRow row, final String column, final String value) {
|
||||
final CSVValue csvValue = row.addValue(column, value);
|
||||
csvValue.valueProperty().addListener(observable -> {
|
||||
if (validator.needsColumnValidation(column)) {
|
||||
revalidate();
|
||||
} else {
|
||||
csvValue.setValidationError(validator.isValid(row.getRowNumber(), column, csvValue.getValue()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the column headers as string array
|
||||
*
|
||||
* @param header the headers of the columns
|
||||
*/
|
||||
public void setHeader(String[] header) {
|
||||
@@ -86,6 +107,7 @@ public class CSVModel {
|
||||
|
||||
/**
|
||||
* returns the column headers
|
||||
*
|
||||
* @return the column headers
|
||||
*/
|
||||
public String[] getHeader() {
|
||||
@@ -93,41 +115,49 @@ public class CSVModel {
|
||||
}
|
||||
|
||||
|
||||
public void revalidate(String column) {
|
||||
if (!hasValidator()) return;
|
||||
validator.reinitializeColumn(column);
|
||||
revalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* walks through the data and validates each value
|
||||
*/
|
||||
public void revalidate() {
|
||||
validationError.clear();
|
||||
|
||||
if (header != null && validator != null) {
|
||||
addValidationError(validator.isHeaderValid(header));
|
||||
logger.info("revalidate: hasValidator -> {}", hasValidator());
|
||||
|
||||
if (!hasValidator()) return;
|
||||
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();
|
||||
}
|
||||
|
||||
for (int lineNumber = 0; lineNumber < rows.size(); lineNumber++) {
|
||||
CSVRow row = rows.get(lineNumber);
|
||||
row.setValidator(validator);
|
||||
for (String column: row.getColumns().keySet()) {
|
||||
CSVValue value = row.getColumns().get(column).getValue();
|
||||
value.setValidator(validator);
|
||||
if (validator != null) {
|
||||
ValidationError validationError = validator.isValid(column, value.getValue(), lineNumber);
|
||||
if (validationError != null) {
|
||||
addValidationError(validationError);
|
||||
value.setValidationError(validationError);
|
||||
} else {
|
||||
value.setValidationError(null);
|
||||
}
|
||||
} else {
|
||||
value.setValidationError(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
private boolean hasValidator() {
|
||||
return validator != null && validator.hasConfig();
|
||||
}
|
||||
|
||||
private void addValidationError(ValidationError validationError) {
|
||||
if (validationError != null) {
|
||||
this.validationError.add(validationError);
|
||||
}
|
||||
public ValidationConfiguration createValidationConfiguration() {
|
||||
ValidationConfiguration newValidationConfiguration = new ValidationConfiguration();
|
||||
newValidationConfiguration.setHeaderNames(this.header);
|
||||
this.validator = new Validator(newValidationConfiguration, this);
|
||||
this.revalidate();
|
||||
return newValidationConfiguration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getValue(int row, String column) {
|
||||
return rows.get(row).getColumns().get(column).getValue().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfRows() {
|
||||
return rows.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,24 +30,14 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableMap;
|
||||
import ninja.javafx.smartcsv.validation.Validator;
|
||||
|
||||
/**
|
||||
* This class represents a single row in the csv file.
|
||||
*/
|
||||
public class CSVRow {
|
||||
private Validator validator;
|
||||
private ObservableMap<String, ObjectProperty<CSVValue>> columns = FXCollections.observableHashMap();
|
||||
private int rowNumber;
|
||||
|
||||
/**
|
||||
* single row
|
||||
* @param validator the reference to the validator
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the row number
|
||||
* @param rowNumber
|
||||
@@ -78,13 +68,11 @@ public class CSVRow {
|
||||
* @param column column name
|
||||
* @param value the value to store
|
||||
*/
|
||||
public void addValue(String column, String value) {
|
||||
CSVValue addValue(String column, String value) {
|
||||
CSVValue v = new CSVValue();
|
||||
v.setValidator(validator);
|
||||
v.setValue(value);
|
||||
v.setColumn(column);
|
||||
v.setRowNumber(rowNumber);
|
||||
columns.put(column, new SimpleObjectProperty<>(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ package ninja.javafx.smartcsv.fx.table.model;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
import ninja.javafx.smartcsv.validation.Validator;
|
||||
|
||||
/**
|
||||
* The csv value represents the value of a single cell.
|
||||
@@ -37,36 +36,9 @@ import ninja.javafx.smartcsv.validation.Validator;
|
||||
* and if the value is valid based on the validator.
|
||||
*/
|
||||
public class CSVValue {
|
||||
private Validator validator;
|
||||
private int rowNumber;
|
||||
private String column;
|
||||
private StringProperty value = new SimpleStringProperty();
|
||||
private ValidationError valid;
|
||||
|
||||
/**
|
||||
* single value of a cell
|
||||
* @param validator the reference to the validator
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* the row number this value is stored in
|
||||
* @param row row number
|
||||
*/
|
||||
public void setRowNumber(int row) {
|
||||
this.rowNumber = row;
|
||||
}
|
||||
|
||||
/**
|
||||
* the column this value is stored in
|
||||
* @param column header name of the column
|
||||
*/
|
||||
public void setColumn(String column) {
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the real value
|
||||
* @return the real value
|
||||
@@ -88,9 +60,6 @@ public class CSVValue {
|
||||
* @param value the real value
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
if (validator != null) {
|
||||
valid = validator.isValid(column, value, rowNumber);
|
||||
}
|
||||
this.value.set(value);
|
||||
}
|
||||
|
||||
@@ -106,7 +75,7 @@ public class CSVValue {
|
||||
* sets the state if a value is valid or not
|
||||
* @param valid the validation state
|
||||
*/
|
||||
protected void setValidationError(ValidationError valid) {
|
||||
public void setValidationError(ValidationError valid) {
|
||||
this.valid = valid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package ninja.javafx.smartcsv.fx.table.model;
|
||||
|
||||
/**
|
||||
* interface for easier access to values in a column
|
||||
*/
|
||||
public interface ColumnValueProvider {
|
||||
|
||||
String getValue(int row, String column);
|
||||
int getNumberOfRows();
|
||||
|
||||
}
|
||||
@@ -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 = "#ff3333";
|
||||
}
|
||||
@@ -40,15 +40,38 @@ import static java.text.MessageFormat.format;
|
||||
*/
|
||||
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 getI18nValidatioMessageWithColumn(ResourceBundle resourceBundle, ValidationError error) {
|
||||
return getI18nValidatioMessage(resourceBundle, error, resourceBundle.getString("column") + " " + error.getColumn() + " : ");
|
||||
}
|
||||
|
||||
public static String getI18nValidatioMessage(ResourceBundle resourceBundle, ValidationError error) {
|
||||
return getI18nValidatioMessage(resourceBundle, error, "");
|
||||
}
|
||||
|
||||
private static String getI18nValidatioMessage(ResourceBundle resourceBundle, ValidationError error, String prefix) {
|
||||
|
||||
List<ValidationMessage> validationMessages = error.getMessages();
|
||||
StringWriter message = new StringWriter();
|
||||
for (ValidationMessage validationMessage: validationMessages) {
|
||||
message.append(prefix);
|
||||
if (resourceBundle.containsKey(validationMessage.getKey())) {
|
||||
String resourceText = resourceBundle.getString(validationMessage.getKey());
|
||||
if (validationMessage.getParameters().length > 0) {
|
||||
message.append(format(resourceText, validationMessage.getParameters())).append("\n");
|
||||
message.append(format(resourceText, (Object[]) validationMessage.getParameters())).append("\n");
|
||||
} else {
|
||||
message.append(resourceText).append("\n");
|
||||
}
|
||||
|
||||
@@ -28,9 +28,7 @@ package ninja.javafx.smartcsv.fx.util;
|
||||
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
import ninja.javafx.smartcsv.FileReader;
|
||||
|
||||
import java.io.File;
|
||||
import ninja.javafx.smartcsv.files.FileStorage;
|
||||
|
||||
/**
|
||||
* Service class for async load of a csv file
|
||||
@@ -38,14 +36,10 @@ import java.io.File;
|
||||
@org.springframework.stereotype.Service
|
||||
public class LoadFileService extends Service {
|
||||
|
||||
private File file;
|
||||
private FileReader fileReader;
|
||||
private FileStorage file;
|
||||
|
||||
public void setFile(File value) {
|
||||
file = value;
|
||||
}
|
||||
public void setFileReader(FileReader fileReader) {
|
||||
this.fileReader = fileReader;
|
||||
public void setFileStorage(FileStorage file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +48,7 @@ public class LoadFileService extends Service {
|
||||
@Override
|
||||
protected Void call() throws Exception {
|
||||
if (file != null) {
|
||||
fileReader.read(file);
|
||||
file.load();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,7 @@ package ninja.javafx.smartcsv.fx.util;
|
||||
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
import ninja.javafx.smartcsv.FileWriter;
|
||||
import ninja.javafx.smartcsv.csv.CSVFileWriter;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static javafx.application.Platform.runLater;
|
||||
import ninja.javafx.smartcsv.files.FileStorage;
|
||||
|
||||
/**
|
||||
* Service class for async load of a csv file
|
||||
@@ -41,14 +36,9 @@ import static javafx.application.Platform.runLater;
|
||||
@org.springframework.stereotype.Service
|
||||
public class SaveFileService extends Service {
|
||||
|
||||
private File file;
|
||||
private FileWriter writer;
|
||||
private FileStorage file;
|
||||
|
||||
public void setWriter(FileWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public void setFile(File value) {
|
||||
public void setFileStorage(FileStorage value) {
|
||||
file = value;
|
||||
}
|
||||
|
||||
@@ -58,7 +48,7 @@ public class SaveFileService extends Service {
|
||||
@Override
|
||||
protected Void call() throws Exception {
|
||||
try {
|
||||
writer.write(file);
|
||||
file.save();
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
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 CheckBox uniqueRuleCheckBox;
|
||||
|
||||
@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;
|
||||
|
||||
@FXML
|
||||
private CheckBox enableUniqueRule;
|
||||
|
||||
|
||||
@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);
|
||||
initCheckBox(uniqueRuleCheckBox, enableUniqueRule);
|
||||
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 (enableUniqueRule.isSelected()) {
|
||||
validationConfiguration.setUniqueRuleFor(selectedColumn.getValue(), uniqueRuleCheckBox.isSelected());
|
||||
} else {
|
||||
validationConfiguration.setUniqueRuleFor(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
|
||||
);
|
||||
|
||||
updateCheckBox(
|
||||
uniqueRuleCheckBox,
|
||||
validationConfiguration.getUniqueRuleFor(getSelectedColumn()),
|
||||
enableUniqueRule
|
||||
);
|
||||
|
||||
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,25 +26,24 @@
|
||||
|
||||
package ninja.javafx.smartcsv.preferences;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import ninja.javafx.smartcsv.FileReader;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.supercsv.prefs.CsvPreference;
|
||||
import org.supercsv.quote.AlwaysQuoteMode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static ninja.javafx.smartcsv.preferences.QuoteModeHelper.getQuoteMode;
|
||||
|
||||
/**
|
||||
* file reader for the preferences
|
||||
*/
|
||||
@Service
|
||||
public class PreferencesFileReader implements FileReader {
|
||||
public class PreferencesFileReader implements FileReader<CsvPreference> {
|
||||
|
||||
private Config config;
|
||||
private Map config;
|
||||
private CsvPreference csvPreference;
|
||||
|
||||
public PreferencesFileReader() {
|
||||
@@ -55,15 +54,15 @@ public class PreferencesFileReader implements FileReader {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
char quoteChar = config.getString("quoteChar").charAt(0);
|
||||
char delimiterChar = config.getString("delimiterChar").charAt(0);
|
||||
String endOfLineSymbols = config.getString("endOfLineSymbols");
|
||||
boolean surroundingSpacesNeedQuotes = config.getBoolean("surroundingSpacesNeedQuotes");
|
||||
boolean ignoreEmptyLines = config.getBoolean("ignoreEmptyLines");
|
||||
String quoteMode = config.getString("quoteMode");
|
||||
char quoteChar = config.get("quoteChar").toString().charAt(0);
|
||||
char delimiterChar = config.get("delimiterChar").toString().charAt(0);
|
||||
String endOfLineSymbols = config.get("endOfLineSymbols").toString();
|
||||
boolean surroundingSpacesNeedQuotes = (Boolean)config.get("surroundingSpacesNeedQuotes");
|
||||
boolean ignoreEmptyLines = (Boolean)config.get("ignoreEmptyLines");
|
||||
String quoteMode = config.get("quoteMode").toString();
|
||||
|
||||
csvPreference = new CsvPreference.Builder(quoteChar, delimiterChar, endOfLineSymbols)
|
||||
.useQuoteMode(getQuoteMode(quoteMode))
|
||||
@@ -73,7 +72,7 @@ public class PreferencesFileReader implements FileReader {
|
||||
}
|
||||
}
|
||||
|
||||
public CsvPreference getCSVpreference() {
|
||||
public CsvPreference getContent() {
|
||||
return csvPreference;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,11 +26,9 @@
|
||||
|
||||
package ninja.javafx.smartcsv.preferences;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
import com.typesafe.config.ConfigRenderOptions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import ninja.javafx.smartcsv.FileWriter;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.supercsv.prefs.CsvPreference;
|
||||
|
||||
import java.io.File;
|
||||
@@ -42,12 +40,11 @@ import java.util.Map;
|
||||
/**
|
||||
* Save preferences to configuration file
|
||||
*/
|
||||
@Service
|
||||
public class PreferencesFileWriter implements FileWriter {
|
||||
public class PreferencesFileWriter implements FileWriter<CsvPreference> {
|
||||
|
||||
private CsvPreference csvPreference;
|
||||
|
||||
public void setCsvPreference(CsvPreference csvPreference) {
|
||||
public void setContent(CsvPreference csvPreference) {
|
||||
this.csvPreference = csvPreference;
|
||||
}
|
||||
|
||||
@@ -60,8 +57,8 @@ public class PreferencesFileWriter implements FileWriter {
|
||||
preferences.put("ignoreEmptyLines", csvPreference.isIgnoreEmptyLines());
|
||||
preferences.put("quoteMode", QuoteModeHelper.getQuoteModeName(csvPreference.getQuoteMode()));
|
||||
|
||||
Config config = ConfigFactory.parseMap(preferences);
|
||||
Files.write(file.toPath(), config.root().render(ConfigRenderOptions.concise()).getBytes());
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
Files.write(file.toPath(), gson.toJson(preferences).getBytes());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 static org.apache.commons.validator.GenericValidator.matchRegexp;
|
||||
|
||||
/**
|
||||
* Checks if the value is alpha numeric
|
||||
*/
|
||||
public class AlphaNumericValidation extends EmptyValueIsValid {
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!matchRegexp(value, "[0-9a-zA-Z]*")) {
|
||||
error.add("validation.message.alphanumeric");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.ALPHANUMERIC;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 static org.apache.commons.validator.GenericValidator.isDate;
|
||||
|
||||
/**
|
||||
* Checks if the date has the right format
|
||||
*/
|
||||
public class DateValidation extends EmptyValueIsValid {
|
||||
|
||||
private String dateformat;
|
||||
|
||||
public DateValidation(String dateformat) {
|
||||
assert dateformat != null && !dateformat.trim().isEmpty() : "empty date format for date validation";
|
||||
this.dateformat = dateformat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!isDate(value, dateformat, true)) {
|
||||
error.add("validation.message.date.format", dateformat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.DATE;
|
||||
}
|
||||
}
|
||||
@@ -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 static org.apache.commons.validator.GenericValidator.isDouble;
|
||||
|
||||
/**
|
||||
* Checks if the value is a double
|
||||
*/
|
||||
public class DoubleValidation extends EmptyValueIsValid {
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!isDouble(value)) {
|
||||
error.add("validation.message.double");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ninja.javafx.smartcsv.validation;
|
||||
|
||||
/**
|
||||
* validations based on this are not validated when the value is null or empty
|
||||
*/
|
||||
public abstract class EmptyValueIsValid implements Validation {
|
||||
|
||||
@Override
|
||||
public boolean canBeChecked(String value) {
|
||||
return value != null && !value.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 groovy.lang.Binding;
|
||||
import groovy.lang.GroovyShell;
|
||||
import groovy.lang.Script;
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
|
||||
/**
|
||||
* Executes the given groovy as check
|
||||
*/
|
||||
public class GroovyValidation extends EmptyValueIsValid {
|
||||
|
||||
private String groovyScript;
|
||||
private GroovyShell shell = new GroovyShell();
|
||||
private Script script;
|
||||
|
||||
public GroovyValidation(String groovyScript) {
|
||||
this.groovyScript = groovyScript;
|
||||
script = shell.parse(groovyScript);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
Binding binding = new Binding();
|
||||
binding.setVariable("value", value);
|
||||
script.setBinding(binding);
|
||||
|
||||
Object groovyResult = null;
|
||||
try {
|
||||
groovyResult = script.run();
|
||||
} catch (CompilationFailedException e) {
|
||||
error.add("validation.message.groovy.exception", groovyScript, e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (groovyResult == null) {
|
||||
error.add("validation.message.groovy.return.null", groovyScript);
|
||||
}
|
||||
|
||||
if (!isScriptResultTrue(groovyResult)) {
|
||||
error.add(groovyResult.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.GROOVY;
|
||||
}
|
||||
|
||||
private boolean isScriptResultTrue(Object groovyResult) {
|
||||
return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true");
|
||||
}
|
||||
}
|
||||
@@ -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,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 static org.apache.commons.validator.GenericValidator.isInt;
|
||||
|
||||
/**
|
||||
* Checks if the value is an integer
|
||||
*/
|
||||
public class IntegerValidation extends EmptyValueIsValid {
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!isInt(value)) {
|
||||
error.add("validation.message.integer");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.INTEGER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 static org.apache.commons.validator.GenericValidator.maxLength;
|
||||
|
||||
/**
|
||||
* Checks if the value is shorter or exactly as long as the given max length
|
||||
*/
|
||||
public class MaxLengthValidation extends EmptyValueIsValid {
|
||||
|
||||
private int maxLength;
|
||||
|
||||
public MaxLengthValidation(int maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!maxLength(value, maxLength)) {
|
||||
error.add("validation.message.max.length", Integer.toString(maxLength));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.MAX_LENGTH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 static org.apache.commons.validator.GenericValidator.minLength;
|
||||
|
||||
/**
|
||||
* Checks if the value is at minimum long as the given min length
|
||||
*/
|
||||
public class MinLengthValidation extends EmptyValueIsValid {
|
||||
|
||||
private int minLength;
|
||||
|
||||
public MinLengthValidation(int minLength) {
|
||||
this.minLength = minLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!minLength(value, minLength)) {
|
||||
error.add("validation.message.min.length", Integer.toString(minLength));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.MIN_LENGTH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 static org.apache.commons.validator.GenericValidator.isBlankOrNull;
|
||||
|
||||
/**
|
||||
* Checks if the value is not empty
|
||||
*/
|
||||
public class NotEmptyValidation implements Validation {
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (isBlankOrNull(value)) {
|
||||
error.add("validation.message.not.empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.NOT_EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeChecked(String value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 static org.apache.commons.validator.GenericValidator.matchRegexp;
|
||||
|
||||
/**
|
||||
* Checks the value against the given reg exp
|
||||
*/
|
||||
public class RegExpValidation extends EmptyValueIsValid {
|
||||
|
||||
private String regexp;
|
||||
|
||||
public RegExpValidation(String regexp) {
|
||||
this.regexp = regexp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!matchRegexp(value, regexp)) {
|
||||
error.add("validation.message.regexp", regexp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.REGEXP;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 javafx.beans.property.ObjectProperty;
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
||||
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Service for running the validation async of the ui thread
|
||||
*/
|
||||
public class RevalidationService extends Service<List<ValidationError>> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(RevalidationService.class);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int maxRows = rows.size();
|
||||
for (int lineNumber = 0; lineNumber < maxRows; lineNumber++) {
|
||||
CSVRow row = rows.get(lineNumber);
|
||||
|
||||
Map<String, ObjectProperty<CSVValue>> table = row.getColumns();
|
||||
Set<String> columns = table.keySet();
|
||||
|
||||
for (String column : columns) {
|
||||
CSVValue value = table.get(column).getValue();
|
||||
if (validator != null) {
|
||||
ValidationError validationError = validator.isValid(lineNumber, column, value.getValue());
|
||||
if (validationError != null) {
|
||||
logger.info("revalidate: {} errors found in line {}", validationError.getMessages().size(), lineNumber);
|
||||
errors.add(validationError);
|
||||
value.setValidationError(validationError);
|
||||
} else {
|
||||
value.setValidationError(null);
|
||||
}
|
||||
} else {
|
||||
value.setValidationError(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Throwable t) {
|
||||
logger.error("validation error", t);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 ninja.javafx.smartcsv.fx.table.model.ColumnValueProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.sort;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
/**
|
||||
* Checks if the value is unique in the column
|
||||
*/
|
||||
public class UniqueValidation extends EmptyValueIsValid {
|
||||
|
||||
private ColumnValueProvider columnValueProvider;
|
||||
private String column;
|
||||
|
||||
public UniqueValidation(ColumnValueProvider columnValueProvider, String column) {
|
||||
this.columnValueProvider = columnValueProvider;
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
|
||||
List<Integer> lineNumbers = new ArrayList<>();
|
||||
|
||||
int numberOfRows = columnValueProvider.getNumberOfRows();
|
||||
for (int currentRowOfIteration = 0; currentRowOfIteration < numberOfRows; currentRowOfIteration++) {
|
||||
String storedValue = columnValueProvider.getValue(currentRowOfIteration, column);
|
||||
|
||||
if (value.equals(storedValue) && currentRowOfIteration != row) {
|
||||
lineNumbers.add(currentRowOfIteration + 1); // show not 0 based line numbers to user
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineNumbers.isEmpty()) {
|
||||
if (lineNumbers.size() > 1) {
|
||||
sort(lineNumbers);
|
||||
error.add("validation.message.uniqueness.multiple", value, lineNumbers.stream().map(Object::toString).collect(joining(", ")));
|
||||
} else {
|
||||
error.add("validation.message.uniqueness.single", value, lineNumbers.get(0).toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.UNIQUE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/**
|
||||
* Interface for all validations
|
||||
*/
|
||||
public interface Validation {
|
||||
|
||||
enum Type { NOT_EMPTY, UNIQUE, DOUBLE, INTEGER, MIN_LENGTH, MAX_LENGTH, DATE, ALPHANUMERIC, REGEXP, VALUE_OF, GROOVY }
|
||||
void check(int row, String value, ValidationError error);
|
||||
Type getType();
|
||||
boolean canBeChecked(String value);
|
||||
}
|
||||
@@ -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.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 getUniqueRuleFor(String column) {
|
||||
return (Boolean)getValue(column, "unique");
|
||||
}
|
||||
|
||||
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) {
|
||||
Double value = (Double)getValue(column, "maxlength");
|
||||
return value != null ? doubleToInteger(value) : null;
|
||||
}
|
||||
|
||||
public String getDateRuleFor(String column) {
|
||||
return (String)getValue(column, "date");
|
||||
}
|
||||
|
||||
public Boolean getAlphanumericRuleFor(String column) {
|
||||
return (Boolean)getValue(column, "alphanumeric");
|
||||
}
|
||||
|
||||
public String getRegexpRuleFor(String column) {
|
||||
return (String)getValue(column, "regexp");
|
||||
}
|
||||
|
||||
public List<String> getValueOfRuleFor(String column) {
|
||||
return (List<String>)getValue(column, "value of");
|
||||
}
|
||||
|
||||
public String getGroovyRuleFor(String column) {
|
||||
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 setUniqueRuleFor(String column, Boolean value) {
|
||||
setValue(column, value, "unique");
|
||||
}
|
||||
|
||||
|
||||
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 (columnConfigurations != null) {
|
||||
Map rulesForColumn = columnConfigurations.get(column);
|
||||
if (rulesForColumn != null) {
|
||||
return columnConfigurations.get(column).get(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean noHeader() {
|
||||
return headerConfiguration == null;
|
||||
}
|
||||
|
||||
private Integer doubleToInteger(Double value) {
|
||||
if (value == null) return null;
|
||||
return (int)Math.round(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,6 +38,7 @@ public class ValidationError {
|
||||
|
||||
private List<ValidationMessage> messages = new ArrayList<>();
|
||||
private Integer lineNumber;
|
||||
private String column = "";
|
||||
|
||||
private ValidationError(Integer lineNumber) {
|
||||
this.lineNumber = lineNumber;
|
||||
@@ -51,10 +52,19 @@ public class ValidationError {
|
||||
return new ValidationError(-1);
|
||||
}
|
||||
|
||||
public ValidationError column(String column) {
|
||||
this.column = column;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public String getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
public List<ValidationMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -26,10 +26,8 @@
|
||||
|
||||
package ninja.javafx.smartcsv.validation;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import ninja.javafx.smartcsv.FileReader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -37,17 +35,16 @@ import java.io.IOException;
|
||||
/**
|
||||
* This class loads the constraints as json config
|
||||
*/
|
||||
@Service
|
||||
public class ValidationFileReader implements FileReader {
|
||||
public class ValidationFileReader implements FileReader<ValidationConfiguration> {
|
||||
|
||||
private Config config;
|
||||
private ValidationConfiguration config;
|
||||
|
||||
@Override
|
||||
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() {
|
||||
return new Validator(config);
|
||||
public ValidationConfiguration getContent() {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,47 +24,30 @@
|
||||
|
||||
*/
|
||||
|
||||
package ninja.javafx.smartcsv.fx.list;
|
||||
package ninja.javafx.smartcsv.validation;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.text.Text;
|
||||
import ninja.javafx.smartcsv.validation.ValidationError;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import ninja.javafx.smartcsv.FileWriter;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
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) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
public void setContent(ValidationConfiguration validationConfiguration) {
|
||||
this.validationConfiguration = validationConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(ValidationError validationError, boolean empty) {
|
||||
super.updateItem(validationError, empty);
|
||||
if (empty) {
|
||||
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);
|
||||
public void write(File file) throws IOException {
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
Files.write(file.toPath(), gson.toJson(validationConfiguration).getBytes());
|
||||
}
|
||||
}
|
||||
@@ -26,19 +26,12 @@
|
||||
|
||||
package ninja.javafx.smartcsv.validation;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import groovy.lang.Binding;
|
||||
import groovy.lang.GroovyShell;
|
||||
import groovy.lang.Script;
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
import ninja.javafx.smartcsv.fx.table.model.ColumnValueProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static org.apache.commons.validator.GenericValidator.*;
|
||||
|
||||
/**
|
||||
* This class checks all the validations defined in the
|
||||
* Config against a given value
|
||||
@@ -49,10 +42,9 @@ public class Validator {
|
||||
// member variables
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Config validationConfig;
|
||||
private GroovyShell shell = new GroovyShell();
|
||||
private Map<String, Script> scriptCache = new HashMap<>();
|
||||
|
||||
private ValidationConfiguration validationConfig;
|
||||
private ColumnValueProvider columnValueProvider;
|
||||
private Map<String, Map<Validation.Type, Validation>> columnValidationMap = new HashMap<>();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructors
|
||||
@@ -60,221 +52,171 @@ public class Validator {
|
||||
|
||||
/**
|
||||
* JSON configuration for this validator
|
||||
*
|
||||
* @param validationConfig
|
||||
*/
|
||||
public Validator(Config validationConfig) {
|
||||
public Validator(ValidationConfiguration validationConfig, ColumnValueProvider columnValueProvider) {
|
||||
this.validationConfig = validationConfig;
|
||||
this.columnValueProvider = columnValueProvider;
|
||||
initColumnValidations();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// oublic methods
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean needsColumnValidation(String column) {
|
||||
Map<Validation.Type, Validation> validationMap = columnValidationMap.get(column);
|
||||
if (validationMap != null) {
|
||||
return validationMap.containsKey(Validation.Type.UNIQUE);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checks if the value is valid for the column configuration
|
||||
*
|
||||
* @param column the column name
|
||||
* @param value the value to check
|
||||
* @return ValidationError with information if valid and if not which getMessage happened
|
||||
*/
|
||||
public ValidationError isValid(String column, String value, Integer lineNumber) {
|
||||
public ValidationError isValid(Integer row, String column, String value) {
|
||||
ValidationError result = null;
|
||||
if (validationConfig != null) {
|
||||
Config columnSectionConfig = getColumnSectionConfig();
|
||||
if (columnSectionConfig != null) {
|
||||
Config columnConfig = getColumnConfig(columnSectionConfig, column);
|
||||
if (columnConfig != null) {
|
||||
|
||||
ValidationError error = ValidationError.withLineNumber(lineNumber);
|
||||
checkBlankOrNull(columnConfig, value, error);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
checkRegularExpression(columnConfig, value, error);
|
||||
checkAlphaNumeric(columnConfig, value, error);
|
||||
checkDate(columnConfig, value, error);
|
||||
checkMaxLength(columnConfig, value, error);
|
||||
checkMinLength(columnConfig, value, error);
|
||||
checkInteger(columnConfig, value, error);
|
||||
checkGroovy(column, columnConfig, value, error);
|
||||
checkValueOf(columnConfig, value, error);
|
||||
checkDouble(columnConfig, value, error);
|
||||
if (hasConfig()) {
|
||||
ValidationError error = ValidationError.withLineNumber(row).column(column);
|
||||
Map<Validation.Type, Validation> validationMap = columnValidationMap.get(column);
|
||||
if (validationMap != null) {
|
||||
for (Validation validation: validationMap.values()) {
|
||||
if (validation.canBeChecked(value)) {
|
||||
validation.check(row, value, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
result = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public boolean hasConfig() {
|
||||
return validationConfig != null;
|
||||
}
|
||||
|
||||
public void reinitializeColumn(String column) {
|
||||
clear(column);
|
||||
initializeColumnWithRules(column);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// private methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void checkGroovy(String column, Config columnConfig, String value, ValidationError error) {
|
||||
String groovyScript = getString(columnConfig, "groovy");
|
||||
if (groovyScript != null) {
|
||||
|
||||
Script script = scriptCache.get(column);
|
||||
if (script == null) {
|
||||
script = shell.parse(groovyScript);
|
||||
scriptCache.put(column, script);
|
||||
}
|
||||
|
||||
Binding binding = new Binding();
|
||||
binding.setVariable("value", value);
|
||||
script.setBinding(binding);
|
||||
|
||||
Object groovyResult = null;
|
||||
try {
|
||||
groovyResult = script.run();
|
||||
} catch (CompilationFailedException e) {
|
||||
error.add("validation.message.groovy.exception", groovyScript, e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (groovyResult == null) {
|
||||
error.add("validation.message.groovy.return.null", groovyScript);
|
||||
}
|
||||
|
||||
if (!isScriptResultTrue(groovyResult)) {
|
||||
error.add(groovyResult.toString());
|
||||
private void add(String column, Validation validation) {
|
||||
Map<Validation.Type, Validation> validationMap = columnValidationMap.get(column);
|
||||
if (validationMap == null) {
|
||||
validationMap = new HashMap<>();
|
||||
columnValidationMap.put(column, validationMap);
|
||||
}
|
||||
validationMap.put(validation.getType(), validation);
|
||||
}
|
||||
|
||||
private void clear(String column) {
|
||||
Map<Validation.Type, Validation> validationMap = columnValidationMap.get(column);
|
||||
if (validationMap != null) {
|
||||
validationMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isScriptResultTrue(Object groovyResult) {
|
||||
return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true");
|
||||
}
|
||||
|
||||
private void checkValueOf(Config columnConfig, String value, ValidationError error) {
|
||||
List<String> stringList = getStringList(columnConfig, "value of");
|
||||
if (stringList != null) {
|
||||
if (!stringList.contains(value)) {
|
||||
String commaSeparated = stringList.stream().collect(joining(", "));
|
||||
error.add("validation.message.value.of", value, commaSeparated);
|
||||
private void initColumnValidations() {
|
||||
if (hasConfig()) {
|
||||
String[] columns = validationConfig.headerNames();
|
||||
for (String column : columns) {
|
||||
initializeColumnWithRules(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBlankOrNull(Config columnConfig, String value, ValidationError error) {
|
||||
if (getBoolean(columnConfig, "not empty")) {
|
||||
if (isBlankOrNull(value)) {
|
||||
error.add("validation.message.not.empty");
|
||||
}
|
||||
}
|
||||
private void initializeColumnWithRules(String column) {
|
||||
Boolean alphaNumeric = validationConfig.getAlphanumericRuleFor(column);
|
||||
if (alphaNumeric != null && alphaNumeric) {
|
||||
add(column, new AlphaNumericValidation());
|
||||
}
|
||||
|
||||
private void checkInteger(Config columnConfig, String value, ValidationError error) {
|
||||
if (getBoolean(columnConfig, "integer")) {
|
||||
if (!isInt(value)) {
|
||||
error.add("validation.message.integer");
|
||||
}
|
||||
}
|
||||
Boolean doubleRule = validationConfig.getDoubleRuleFor(column);
|
||||
if (doubleRule != null && doubleRule) {
|
||||
add(column, new DoubleValidation());
|
||||
}
|
||||
|
||||
private void checkDouble(Config columnConfig, String value, ValidationError error) {
|
||||
if (getBoolean(columnConfig, "double")) {
|
||||
if (!isDouble(value)) {
|
||||
error.add("validation.message.double");
|
||||
}
|
||||
}
|
||||
Boolean integerRule = validationConfig.getIntegerRuleFor(column);
|
||||
if (integerRule != null && integerRule) {
|
||||
add(column, new IntegerValidation());
|
||||
}
|
||||
|
||||
Boolean notEmptyRule = validationConfig.getNotEmptyRuleFor(column);
|
||||
if (notEmptyRule != null && notEmptyRule) {
|
||||
add(column, new NotEmptyValidation());
|
||||
}
|
||||
|
||||
private void checkMinLength(Config columnConfig, String value, ValidationError error) {
|
||||
Integer minLength = getInteger(columnConfig, "minlength");
|
||||
Boolean uniqueRule = validationConfig.getUniqueRuleFor(column);
|
||||
if (uniqueRule != null && uniqueRule) {
|
||||
add(column, new UniqueValidation(columnValueProvider, column));
|
||||
}
|
||||
|
||||
String dateRule = validationConfig.getDateRuleFor(column);
|
||||
if (dateRule != null && !dateRule.trim().isEmpty()) {
|
||||
add(column, new DateValidation(dateRule));
|
||||
}
|
||||
|
||||
Integer minLength = validationConfig.getMinLengthRuleFor(column);
|
||||
if (minLength != null) {
|
||||
if (!minLength(value, minLength)) {
|
||||
error.add("validation.message.min.length", minLength.toString());
|
||||
}
|
||||
}
|
||||
add(column, new MinLengthValidation(minLength));
|
||||
}
|
||||
|
||||
private void checkMaxLength(Config columnConfig, String value, ValidationError error) {
|
||||
Integer maxLength = getInteger(columnConfig, "maxlength");
|
||||
Integer maxLength = validationConfig.getMaxLengthRuleFor(column);
|
||||
if (maxLength != null) {
|
||||
if (!maxLength(value, maxLength)) {
|
||||
error.add("validation.message.max.length", maxLength.toString());
|
||||
}
|
||||
}
|
||||
add(column, new MaxLengthValidation(maxLength));
|
||||
}
|
||||
|
||||
private void checkDate(Config columnConfig, String value, ValidationError error) {
|
||||
String dateformat = getString(columnConfig, "date");
|
||||
if (dateformat != null && !dateformat.trim().isEmpty()) {
|
||||
if (!isDate(value, dateformat, true)) {
|
||||
error.add("validation.message.date.format", dateformat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAlphaNumeric(Config columnConfig, String value, ValidationError error) {
|
||||
if (getBoolean(columnConfig, "alphanumeric")) {
|
||||
if (!matchRegexp(value, "[0-9a-zA-Z]*")) {
|
||||
error.add("validation.message.alphanumeric");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRegularExpression(Config columnConfig, String value, ValidationError error) {
|
||||
String regexp = getString(columnConfig, "regexp");
|
||||
String regexp = validationConfig.getRegexpRuleFor(column);
|
||||
if (regexp != null && !regexp.trim().isEmpty()) {
|
||||
if (!matchRegexp(value, regexp)) {
|
||||
error.add("validation.message.regexp", regexp);
|
||||
add(column, new RegExpValidation(regexp));
|
||||
}
|
||||
|
||||
String groovy = validationConfig.getGroovyRuleFor(column);
|
||||
if (groovy != null && !groovy.trim().isEmpty()) {
|
||||
add(column, new GroovyValidation(groovy));
|
||||
}
|
||||
List<String> valueOfRule = validationConfig.getValueOfRuleFor(column);
|
||||
if (valueOfRule != null && !valueOfRule.isEmpty()) {
|
||||
add(column, new ValueOfValidation(valueOfRule));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
ValidationError result = null;
|
||||
if (validationConfig != null) {
|
||||
if (validationConfig.hasPath("headers")) {
|
||||
Config headerSectionConfig = validationConfig.getConfig("headers");
|
||||
List<String> headerConfig = getStringList(headerSectionConfig, "list");
|
||||
if (headerConfig != null) {
|
||||
if (headerNames.length != headerConfig.size()) {
|
||||
String[] headerNamesConfig = validationConfig.headerNames();
|
||||
if (headerNamesConfig != null) {
|
||||
if (headerNames.length != headerNamesConfig.length) {
|
||||
result = ValidationError.withoutLineNumber().add("validation.message.header.length",
|
||||
Integer.toString(headerNames.length),
|
||||
Integer.toString(headerConfig.size()));
|
||||
Integer.toString(headerNamesConfig.length));
|
||||
return result;
|
||||
}
|
||||
|
||||
ValidationError error = ValidationError.withoutLineNumber();
|
||||
|
||||
for(int i=0; i<headerConfig.size(); i++) {
|
||||
String header = headerConfig.get(i);
|
||||
if (!header.equals(headerNames[i])) {
|
||||
for (int i = 0; i < headerNamesConfig.length; i++) {
|
||||
if (!headerNamesConfig[i].equals(headerNames[i])) {
|
||||
error.add("validation.message.header.match",
|
||||
Integer.toString(i),
|
||||
header,
|
||||
headerNamesConfig[i],
|
||||
headerNames[i]);
|
||||
}
|
||||
}
|
||||
@@ -283,7 +225,6 @@ public class Validator {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 java.util.List;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
/**
|
||||
* Checks if the value is part of a list of values
|
||||
*/
|
||||
public class ValueOfValidation extends EmptyValueIsValid {
|
||||
|
||||
private List<String> values;
|
||||
|
||||
public ValueOfValidation(List<String> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(int row, String value, ValidationError error) {
|
||||
if (!values.contains(value)) {
|
||||
String commaSeparated = values.stream().collect(joining(", "));
|
||||
error.add("validation.message.value.of", value, commaSeparated);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.VALUE_OF;
|
||||
}
|
||||
}
|
||||
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,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
|
||||
<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">
|
||||
<?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">
|
||||
<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">
|
||||
<VBox.margin>
|
||||
<Insets top="8.0" />
|
||||
@@ -19,7 +35,7 @@
|
||||
</Label>
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true">
|
||||
<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 hgrow="SOMETIMES" minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
|
||||
@@ -33,6 +49,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>
|
||||
<children>
|
||||
<Label text="junit" />
|
||||
@@ -45,12 +64,18 @@
|
||||
<Hyperlink onAction="#openLinkInBrowser" text="https://spring.io/" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||
<Label text="supercsv" 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" />
|
||||
<Hyperlink onAction="#openLinkInBrowser" text="https://github.com/typesafehub/config" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||
<Label text="Gson" 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" />
|
||||
<Hyperlink onAction="#openLinkInBrowser" text="https://commons.apache.org/proper/commons-validator/" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||
<Label text="fontawesomefx" 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>
|
||||
<padding>
|
||||
<Insets bottom="4.0" left="8.0" right="8.0" top="4.0" />
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
application.name = SmartCSV.fx
|
||||
application.version = 0.3
|
||||
application.version = 0.7
|
||||
|
||||
# fxml views
|
||||
fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.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.validation.editor.view = /ninja/javafx/smartcsv/fx/validation/validationEditor.fxml
|
||||
|
||||
# resources
|
||||
resource.main = ninja.javafx.smartcsv.fx.smartcsv
|
||||
@@ -1,10 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<GridPane hgap="8.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" vgap="8.0" xmlns="http://javafx.com/javafx/8.0.66" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
||||
|
||||
@@ -15,36 +15,215 @@
|
||||
}
|
||||
|
||||
.open-icon {
|
||||
-glyph-name: "FILE_TEXT_ALT";
|
||||
-glyph-size: 14px;
|
||||
-glyph-name: "FILE_IMPORT";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.file-document-icon {
|
||||
-glyph-name: "FILE_DOCUMENT";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.config-icon {
|
||||
-glyph-name: "CHECK";
|
||||
-glyph-size: 14px;
|
||||
-glyph-name: "CLIPBOARD";
|
||||
-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 {
|
||||
-glyph-name: "FLOPPY_ALT";
|
||||
-glyph-size: 14px;
|
||||
-glyph-name: "FILE_EXPORT";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.exit-icon {
|
||||
-glyph-name: "SIGN_OUT";
|
||||
-glyph-size: 14px;
|
||||
-glyph-name: "EXIT_TO_APP";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
-glyph-name: "INFO";
|
||||
-glyph-size: 14px;
|
||||
-glyph-name: "INFORMATION_OUTLINE";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.error-title-icon {
|
||||
-glyph-name: "EXCLAMATION_TRIANGLE";
|
||||
-glyph-size: 14px;
|
||||
.config-check-icon {
|
||||
-glyph-name: "CLIPBOARD_CHECK";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.preferences-icon {
|
||||
-glyph-name: "COG";
|
||||
-glyph-size: 14px;
|
||||
-glyph-name: "SETTINGS";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
-glyph-name: "TABLE_ROW_REMOVE";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
-glyph-name: "TABLE_ROW_PLUS_AFTER";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.goto-icon {
|
||||
-glyph-name: "SUBDIRECTORY_ARROW_RIGHT";
|
||||
-glyph-size: 16px;
|
||||
}
|
||||
|
||||
.export-icon {
|
||||
-glyph-name: "BUG";
|
||||
-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;
|
||||
}
|
||||
|
||||
.tool-bar .export-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,267 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import de.jensd.fx.glyphs.fontawesome.*?>
|
||||
<?import de.jensd.fx.glyphs.*?>
|
||||
<?import de.jensd.fx.glyphs.materialdesignicons.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import java.net.URL?>
|
||||
<BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
|
||||
minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.66"
|
||||
xmlns:fx="http://javafx.com/fxml/1">
|
||||
<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.101" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<top>
|
||||
<VBox prefWidth="100.0" BorderPane.alignment="CENTER">
|
||||
<VBox id="background" prefWidth="100.0" BorderPane.alignment="CENTER">
|
||||
<children>
|
||||
<MenuBar>
|
||||
<MenuBar useSystemMenuBar="true">
|
||||
<menus>
|
||||
<Menu mnemonicParsing="false" text="%menu.file">
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="save-icon"/>
|
||||
</graphic>
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#openCsv" text="%menu.open.csv">
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="open-icon"/>
|
||||
<MaterialDesignIconView styleClass="open-icon" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
<MenuItem mnemonicParsing="false" onAction="#openConfig" text="%menu.open.config">
|
||||
<MenuItem fx:id="saveMenuItem" disable="true" mnemonicParsing="false" onAction="#saveCsv" text="%menu.save">
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="config-icon"/>
|
||||
<MaterialDesignIconView styleClass="save-icon" />
|
||||
</graphic>
|
||||
</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" />
|
||||
<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>
|
||||
<FontAwesomeIconView styleClass="save-icon"/>
|
||||
<GlyphsStack>
|
||||
<children>
|
||||
<MaterialDesignIconView styleClass="config-icon" />
|
||||
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="create-config-icon" />
|
||||
</children>
|
||||
</GlyphsStack>
|
||||
</graphic>
|
||||
</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>
|
||||
<FontAwesomeIconView styleClass="save-icon"/>
|
||||
<GlyphsStack>
|
||||
<children>
|
||||
<MaterialDesignIconView styleClass="config-icon" />
|
||||
<MaterialDesignIconView style="-fx-opacity: 0.7;" styleClass="load-config-icon" />
|
||||
</children>
|
||||
</GlyphsStack>
|
||||
</graphic>
|
||||
</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" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#preferences" text="%menu.preferences">
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="preferences-icon"/>
|
||||
<MaterialDesignIconView styleClass="preferences-icon" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
<MenuItem fx:id="exportMenuItem" disable="true" mnemonicParsing="false" onAction="#export" text="%menu.export">
|
||||
<graphic>
|
||||
<MaterialDesignIconView styleClass="export-icon" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
<SeparatorMenuItem mnemonicParsing="false" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#close" text="%menu.close">
|
||||
<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>
|
||||
</MenuItem>
|
||||
<SeparatorMenuItem mnemonicParsing="false" />
|
||||
<MenuItem fx:id="gotoLineMenuItem" disable="true" mnemonicParsing="false" onAction="#gotoLine" text="%menu.goto.line">
|
||||
<graphic>
|
||||
<MaterialDesignIconView styleClass="goto-icon" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
</items>
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="%menu.help">
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="info-icon"/>
|
||||
</graphic>
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#about" text="%menu.about">
|
||||
<graphic>
|
||||
<FontAwesomeIconView styleClass="info-icon"/>
|
||||
<MaterialDesignIconView styleClass="info-icon" />
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
</items>
|
||||
</Menu>
|
||||
</menus>
|
||||
</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 fx:id="exportButton" disable="true" mnemonicParsing="false" onAction="#export">
|
||||
<tooltip>
|
||||
<Tooltip text="%menu.export" />
|
||||
</tooltip>
|
||||
<graphic>
|
||||
<MaterialDesignIconView styleClass="export-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>
|
||||
</VBox>
|
||||
</top>
|
||||
<center>
|
||||
<SplitPane dividerPositions="0.8" prefHeight="160.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||
<items>
|
||||
<AnchorPane fx:id="tableWrapper">
|
||||
<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">
|
||||
<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">
|
||||
<columns>
|
||||
</columns>
|
||||
</TableView>
|
||||
</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>
|
||||
<left>
|
||||
</left>
|
||||
@@ -109,26 +275,27 @@
|
||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0"/>
|
||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||
<ColumnConstraints fillWidth="false" halignment="RIGHT" hgrow="NEVER" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<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 fx:id="csvName" GridPane.columnIndex="2" GridPane.hgrow="ALWAYS" />
|
||||
<Label text="%stateline.state" GridPane.columnIndex="3" GridPane.hgrow="NEVER"/>
|
||||
<Label fx:id="stateName" GridPane.columnIndex="4" GridPane.hgrow="ALWAYS"/>
|
||||
<FontAwesomeIconView styleClass="open-icon" GridPane.columnIndex="5" GridPane.hgrow="NEVER"/>
|
||||
<Label text="%stateline.configuration" GridPane.columnIndex="6" GridPane.hgrow="NEVER"/>
|
||||
<Label fx:id="configurationName" GridPane.columnIndex="7" GridPane.hgrow="ALWAYS"/>
|
||||
<MaterialDesignIconView styleClass="config-check-icon" GridPane.columnIndex="3" GridPane.hgrow="NEVER" />
|
||||
<Label text="%stateline.configuration" GridPane.columnIndex="4" GridPane.hgrow="NEVER" />
|
||||
<Label fx:id="configurationName" GridPane.columnIndex="5" GridPane.hgrow="ALWAYS" />
|
||||
<Label text="%lineNumber" GridPane.columnIndex="7" />
|
||||
<Label fx:id="currentLineNumber" text="" GridPane.columnIndex="8" />
|
||||
</children>
|
||||
<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>
|
||||
</GridPane>
|
||||
</bottom>
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
menu.open.csv = Open CSV File
|
||||
menu.open.config = Open Validation Config
|
||||
menu.create.config = Create Validation Config
|
||||
menu.save = Save
|
||||
menu.save.as = Save As ...
|
||||
menu.save.config = Save Validation Config
|
||||
menu.save.as.config = Save Validation Config as ...
|
||||
menu.close = Close
|
||||
menu.about = About
|
||||
menu.file = File
|
||||
menu.edit = Edit
|
||||
menu.help = Help
|
||||
menu.preferences = Preferences
|
||||
menu.delete.row = Delete row
|
||||
menu.add.row = Add row
|
||||
menu.goto.line = Goto line
|
||||
menu.export = Export error log
|
||||
|
||||
title.validation.errors = Validation Errors:
|
||||
|
||||
stateline.csv = CSV:
|
||||
@@ -24,6 +32,10 @@ dialog.exit.text = There are changes made to the csv file. If you close now, the
|
||||
dialog.preferences.title = Preferences
|
||||
dialog.preferences.header.text = Preferences
|
||||
|
||||
dialog.goto.line.title = Go to line
|
||||
dialog.goto.line.header.text = Go to given line (max. {0}) and select line!
|
||||
dialog.goto.line.label = Line:
|
||||
|
||||
preferences.quoteChar = Quote character:
|
||||
preferences.delimiterChar = Delimiter character:
|
||||
preferences.ignoreEmptyLines = Ignore empty lines:
|
||||
@@ -41,7 +53,35 @@ validation.message.min.length = has not min length of {0}
|
||||
validation.message.max.length = has not max length of {0}
|
||||
validation.message.date.format = is not a date of format {0}
|
||||
validation.message.regexp = does not match {0}
|
||||
validation.message.uniqueness.multiple = value {0} is not unique (found in rows {1})
|
||||
validation.message.uniqueness.single = value {0} is not unique (found in row {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 "{2}"
|
||||
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
|
||||
validation.rule.label.unique = Unique in column:
|
||||
|
||||
dialog.validation.rules.title = Validation rules
|
||||
dialog.validation.rules.header = Validation rules of column "{0}"
|
||||
context.menu.edit.column.rules = Edit rules
|
||||
|
||||
lineNumber = Selected line:
|
||||
|
||||
log.header.message = {0} has {1} errors
|
||||
log.message = row {0} column {1} : {2}
|
||||
|
||||
column = column
|
||||
@@ -8,14 +8,23 @@
|
||||
|
||||
menu.open.csv = CSV Datei \u00f6ffnen
|
||||
menu.open.config = Pr\u00fcfkonfiguration \u00f6ffnen
|
||||
menu.create.config = Pr\u00fcfkonfiguration erzeugen
|
||||
menu.save = Speichern
|
||||
menu.save.as = Speichern als ...
|
||||
menu.save.config = Pr\u00fcfkonfiguration speichern
|
||||
menu.save.as.config = Pr\u00fcfkonfiguration speichern als ...
|
||||
|
||||
menu.close = Beenden
|
||||
menu.about = \u00dcber ...
|
||||
menu.file = Datei
|
||||
menu.edit = Bearbeiten
|
||||
menu.help = Hilfe
|
||||
menu.preferences = Einstellungen
|
||||
menu.delete.row = Zeile l\u00f6schen
|
||||
menu.add.row = Zeile hinzuf\u00fcgen
|
||||
menu.goto.line = Springe zur Zeile
|
||||
menu.export = Export Fehlerdatei
|
||||
|
||||
title.validation.errors = Fehler in der Datei:
|
||||
|
||||
stateline.csv = CSV:
|
||||
@@ -28,6 +37,10 @@ state.unchanged = Unver\u00e4ndert
|
||||
dialog.preferences.title = Eigenschaften
|
||||
dialog.preferences.header.text = Eigenschaften
|
||||
|
||||
dialog.goto.line.title = Springe zur Zeile
|
||||
dialog.goto.line.header.text = Zur angegebenen Zeile (max. {0}) springen und ausw\u00e4hlen!
|
||||
dialog.goto.line.label = Zeile:
|
||||
|
||||
dialog.exit.title = Anwendung beenden
|
||||
dialog.exit.header.text = M\u00f6chten Sie wirklich die Anwendung beenden?
|
||||
dialog.exit.text = Es gibt noch ungespeicherte \u00c4nderungen, die verloren gehen, wenn Sie die Anwendung jetzt beenden.
|
||||
@@ -49,7 +62,36 @@ validation.message.min.length = Hat nicht die minimale L\u00e4nge von {0}
|
||||
validation.message.max.length = Hat nicht die maximale L\u00e4nge von {0}
|
||||
validation.message.date.format = Das Datumsformat entspricht nicht {0}
|
||||
validation.message.regexp = entspricht nicht dem regul\u00e4ren Ausdruck {0}
|
||||
validation.message.uniqueness.multiple = Wert {0} ist nicht einmalig (gefunden in den Zeilen {1})
|
||||
validation.message.uniqueness.single = Wert {0} ist nicht einmalig (gefunden in Zeile {1})
|
||||
|
||||
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.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
|
||||
validation.rule.label.unique = Einmalig in Spalte:
|
||||
|
||||
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
|
||||
|
||||
lineNumber = Ausgew\u00e4hlte Zeile:
|
||||
|
||||
log.header.message = {0} hat {1} Fehler
|
||||
log.message = Zeile {0} Spalte {1} : {2}
|
||||
|
||||
column = Spalte
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?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 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="6" />
|
||||
<Label text="%validation.rule.label.maxlength" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
||||
<Label text="%validation.rule.label.dateformat" GridPane.columnIndex="1" GridPane.rowIndex="8" />
|
||||
<Label text="%validation.rule.label.alphanumeric" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||
<Label text="%validation.rule.label.regexp" GridPane.columnIndex="1" GridPane.rowIndex="9" />
|
||||
<Label text="%validation.rule.label.value_of" GridPane.columnIndex="1" GridPane.rowIndex="10" />
|
||||
<Label text="%validation.rule.label.groovy" GridPane.columnIndex="1" GridPane.rowIndex="11" />
|
||||
<Label text="%validation.rule.label.unique" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||
<CheckBox fx:id="enableNotEmptyRule" mnemonicParsing="false" GridPane.rowIndex="1" />
|
||||
<CheckBox fx:id="notEmptyRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="1" />
|
||||
<CheckBox fx:id="enableIntegerRule" mnemonicParsing="false" GridPane.rowIndex="2" />
|
||||
<CheckBox fx:id="integerRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="2" />
|
||||
<CheckBox fx:id="enableDoubleRule" mnemonicParsing="false" GridPane.rowIndex="3" />
|
||||
<CheckBox fx:id="doublerRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="3" />
|
||||
<CheckBox fx:id="enableAlphanumericRule" mnemonicParsing="false" GridPane.rowIndex="4" />
|
||||
<CheckBox fx:id="alphanumericRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="4" />
|
||||
<CheckBox fx:id="enableUniqueRule" mnemonicParsing="false" GridPane.rowIndex="5" />
|
||||
<CheckBox fx:id="uniqueRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="5" />
|
||||
<CheckBox fx:id="enableMinLengthRule" mnemonicParsing="false" GridPane.rowIndex="6" />
|
||||
<Spinner fx:id="minLengthSpinner" GridPane.columnIndex="2" GridPane.rowIndex="6" />
|
||||
<CheckBox fx:id="enableMaxLengthRule" mnemonicParsing="false" GridPane.rowIndex="7" />
|
||||
<Spinner fx:id="maxLengthSpinner" GridPane.columnIndex="2" GridPane.rowIndex="7" />
|
||||
<CheckBox fx:id="enableDateRule" mnemonicParsing="false" GridPane.rowIndex="8" />
|
||||
<TextField fx:id="dateformatRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="8" />
|
||||
<CheckBox fx:id="enableRegexpRule" mnemonicParsing="false" GridPane.rowIndex="9" />
|
||||
<TextField fx:id="regexpRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="9" />
|
||||
<CheckBox fx:id="enableValueOfRule" mnemonicParsing="false" GridPane.rowIndex="10" />
|
||||
<TextField fx:id="valueOfRuleTextField" GridPane.columnIndex="2" GridPane.rowIndex="10" />
|
||||
<CheckBox fx:id="enableGroovyRule" mnemonicParsing="false" GridPane.rowIndex="11" />
|
||||
<CodeArea fx:id="groovyRuleTextArea" prefHeight="300.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="11" GridPane.rowSpan="2" />
|
||||
<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,12 +26,10 @@
|
||||
|
||||
package ninja.javafx.smartcsv.fx.table.model;
|
||||
|
||||
import ninja.javafx.smartcsv.validation.Validator;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* unit test for the csv model
|
||||
@@ -77,20 +75,6 @@ public class CSVModelTest {
|
||||
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
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package ninja.javafx.smartcsv.fx.table.model;
|
||||
|
||||
import ninja.javafx.smartcsv.validation.Validator;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* unit test for row class
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package ninja.javafx.smartcsv.fx.table.model;
|
||||
|
||||
import ninja.javafx.smartcsv.validation.Validator;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* unit test for the value class
|
||||
*/
|
||||
public class CSVValueTest {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constants
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static final String COLUMN = "COLUMN";
|
||||
static final String VALUE = "VALUE";
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// mocks
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Validator validator = mock(Validator.class);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// subject under test
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
CSVValue sut;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@Before
|
||||
public void initialize() {
|
||||
sut = new CSVValue();
|
||||
sut.setValidator(validator);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// tests
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@Test
|
||||
public void set_value_calls_validation() {
|
||||
//setup
|
||||
sut.setColumn(COLUMN);
|
||||
|
||||
// execution
|
||||
sut.setValue(VALUE);
|
||||
|
||||
// assertion
|
||||
verify(validator).isValid(eq(COLUMN), eq(VALUE), anyInt());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
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.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -52,7 +51,7 @@ public class HeaderValidationTest {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// parameters
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private Config config;
|
||||
private ValidationConfiguration config;
|
||||
private Boolean expectedResult;
|
||||
private List<ValidationMessage> expectedErrors;
|
||||
private String[] headerNames;
|
||||
@@ -66,11 +65,12 @@ public class HeaderValidationTest {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// parameterized constructor
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public HeaderValidationTest(String[] configHeaderNames,
|
||||
public HeaderValidationTest(String configHeaderNames,
|
||||
String[] headerNames,
|
||||
Boolean expectedResult,
|
||||
List<ValidationMessage> expectedErrors) {
|
||||
this.config = headerSectionConfig(configHeaderNames);
|
||||
Gson gson = new GsonBuilder().create();
|
||||
this.config = gson.fromJson(configHeaderNames, ValidationConfiguration.class);
|
||||
this.headerNames = headerNames;
|
||||
this.expectedResult = expectedResult;
|
||||
this.expectedErrors = expectedErrors;
|
||||
@@ -106,11 +106,16 @@ public class HeaderValidationTest {
|
||||
@Parameterized.Parameters
|
||||
public static Collection validationConfigurations() {
|
||||
return asList(new Object[][] {
|
||||
{ new String[] {}, new String[] {}, true, null },
|
||||
{ new String[] {"a"}, new String[] {"a"}, true, null },
|
||||
{ new String[] {"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"))},
|
||||
{ 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(), new String[] {}, true, null },
|
||||
{ json("a"), new String[] {"a"}, true, null },
|
||||
{ json("a"), new String[] {"b"}, false, singletonList(new ValidationMessage("validation.message.header.match", "0", "a", "b"))},
|
||||
{ json("a"), new String[] {"a","b"}, false, singletonList(new ValidationMessage("validation.message.header.length", "2", "1"))},
|
||||
{ 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;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
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.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -23,7 +25,7 @@ public class ValidatorTest {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// parameters
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private Config config;
|
||||
private ValidationConfiguration config;
|
||||
private String column;
|
||||
private String value;
|
||||
private Boolean expectedResult;
|
||||
@@ -39,14 +41,14 @@ public class ValidatorTest {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// parameterized constructor
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public ValidatorTest(String configcolumn,
|
||||
String configValidation,
|
||||
Object configValue,
|
||||
public ValidatorTest(String config,
|
||||
String column,
|
||||
String value,
|
||||
Boolean expectedResult,
|
||||
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.value = value;
|
||||
this.expectedResult = expectedResult;
|
||||
@@ -68,8 +70,10 @@ public class ValidatorTest {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@Test
|
||||
public void validation() {
|
||||
System.out.println(column + " " + value + " " + expectedResult + " " + expectedError);
|
||||
|
||||
// execution
|
||||
ValidationError result = sut.isValid(column, value, 0);
|
||||
ValidationError result = sut.isValid(0, column, value);
|
||||
|
||||
// assertion
|
||||
assertThat(result == null, is(expectedResult));
|
||||
@@ -84,30 +88,42 @@ public class ValidatorTest {
|
||||
@Parameterized.Parameters
|
||||
public static Collection validationConfigurations() {
|
||||
return asList(new Object[][] {
|
||||
{ "column", "not empty", true, "column", "value", true, null },
|
||||
{ "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") },
|
||||
{ "column", "integer", true, "column", "999", true, null },
|
||||
{ "column", "integer", true, "column", "a", false, new ValidationMessage("validation.message.integer") },
|
||||
{ "column", "double", true, "column", "999", true, null },
|
||||
{ "column", "double", true, "column", "999.000", true, null },
|
||||
{ "column", "double", true, "column", "a", false, new ValidationMessage("validation.message.double") },
|
||||
{ "column", "minlength", 2, "column", "12", true, null },
|
||||
{ "column", "minlength", 2, "column", "1", false, new ValidationMessage("validation.message.min.length", "2") },
|
||||
{ "column", "maxlength", 2, "column", "12", true, null },
|
||||
{ "column", "maxlength", 2, "column", "123", false, new ValidationMessage("validation.message.max.length", "2") },
|
||||
{ "column", "date", "yyyyMMdd", "column", "20151127", true, null },
|
||||
{ "column", "date", "yyyyMMdd", "column", "27.11.2015", false, new ValidationMessage("validation.message.date.format", "yyyyMMdd") },
|
||||
{ "column", "alphanumeric", true, "column", "abcABC123", true, null },
|
||||
{ "column", "alphanumeric", true, "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") },
|
||||
{ "column", "regexp", "[a-z]*", "column", "abc", true, null },
|
||||
{ "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 },
|
||||
{ "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 },
|
||||
{ "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", "not empty", true), "column", "value", true, null },
|
||||
{ json("column", "not empty", true), "column", "", false, new ValidationMessage("validation.message.not.empty") },
|
||||
{ json("column", "not empty", true), "column", null, false, new ValidationMessage("validation.message.not.empty") },
|
||||
{ json("column", "integer", true), "column", "999", true, null },
|
||||
{ json("column", "integer", true), "column", "a", false, new ValidationMessage("validation.message.integer") },
|
||||
{ json("column", "double", true), "column", "999", true, null },
|
||||
{ json("column", "double", true), "column", "999.000", true, null },
|
||||
{ json("column", "double", true), "column", "a", false, new ValidationMessage("validation.message.double") },
|
||||
{ json("column", "minlength", 2), "column", "12", true, null },
|
||||
{ json("column", "minlength", 2), "column", "1", false, new ValidationMessage("validation.message.min.length", "2") },
|
||||
{ json("column", "maxlength", 2), "column", "12", true, null },
|
||||
{ json("column", "maxlength", 2), "column", "123", false, new ValidationMessage("validation.message.max.length", "2") },
|
||||
{ json("column", "date", "yyyyMMdd"), "column", "20151127", true, null },
|
||||
{ json("column", "date", "yyyyMMdd"), "column", "27.11.2015", false, new ValidationMessage("validation.message.date.format", "yyyyMMdd") },
|
||||
{ json("column", "alphanumeric", true), "column", "abcABC123", true, null },
|
||||
{ json("column", "alphanumeric", true), "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") },
|
||||
{ json("column", "regexp", "[a-z]*"), "column", "abc", true, null },
|
||||
{ json("column", "regexp", "[a-z]*"), "column", "abcA", false, new ValidationMessage("validation.message.regexp", "[a-z]*") },
|
||||
{ json("column", "groovy", "value.contains('a')? 'true' : 'no a inside'"), "column", "abcdef", true, null },
|
||||
{ json("column", "groovy", "value.contains('a')? 'true' : 'no a inside'"), "column", "bcdefg", false, new ValidationMessage("no a inside") },
|
||||
{ json("column", "value of", asList("a","b","c","d","e")), "column", "c", true, null },
|
||||
{ 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 = "{\"headers\": { \"list\": [\""+column+"\"]},\"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