Merge branch 'editable_column_rules'

This commit is contained in:
Andreas Billmann
2016-02-05 09:44:51 +01:00
22 changed files with 1058 additions and 289 deletions

View File

@@ -21,8 +21,8 @@ dependencies {
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.5' 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.springframework', name:'spring-context', version: '4.2.4.RELEASE'
compile group: 'net.sf.supercsv', name: 'super-csv', version: '2.4.0' compile group: 'net.sf.supercsv', name: 'super-csv', version: '2.4.0'
compile group: 'com.typesafe', name: 'config', version: '1.3.0'
compile group: 'commons-validator', name: 'commons-validator', version: '1.5.0' compile group: 'commons-validator', name: 'commons-validator', version: '1.5.0'
compile group: 'de.jensd', name: 'fontawesomefx', version: '8.8' compile group: 'de.jensd', name: 'fontawesomefx', version: '8.8'
compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.10' compile group: 'org.controlsfx', name: 'controlsfx', version: '8.40.10'
compile group: 'com.google.code.gson', name: 'gson', version: '2.5'
} }

View File

@@ -30,6 +30,9 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ListChangeListener;
import javafx.collections.WeakListChangeListener;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@@ -52,10 +55,12 @@ import ninja.javafx.smartcsv.fx.table.model.CSVRow;
import ninja.javafx.smartcsv.fx.table.model.CSVValue; import ninja.javafx.smartcsv.fx.table.model.CSVValue;
import ninja.javafx.smartcsv.fx.util.LoadFileService; import ninja.javafx.smartcsv.fx.util.LoadFileService;
import ninja.javafx.smartcsv.fx.util.SaveFileService; import ninja.javafx.smartcsv.fx.util.SaveFileService;
import ninja.javafx.smartcsv.fx.validation.ValidationEditorController;
import ninja.javafx.smartcsv.preferences.PreferencesFileReader; import ninja.javafx.smartcsv.preferences.PreferencesFileReader;
import ninja.javafx.smartcsv.preferences.PreferencesFileWriter; import ninja.javafx.smartcsv.preferences.PreferencesFileWriter;
import ninja.javafx.smartcsv.validation.ValidationError; import ninja.javafx.smartcsv.validation.ValidationError;
import ninja.javafx.smartcsv.validation.ValidationFileReader; import ninja.javafx.smartcsv.validation.ValidationFileReader;
import ninja.javafx.smartcsv.validation.ValidationFileWriter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -68,6 +73,7 @@ import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.text.MessageFormat.format;
import static javafx.application.Platform.exit; import static javafx.application.Platform.exit;
import static javafx.application.Platform.runLater; import static javafx.application.Platform.runLater;
import static javafx.beans.binding.Bindings.*; import static javafx.beans.binding.Bindings.*;
@@ -88,6 +94,10 @@ public class SmartCSVController extends FXMLController {
".SmartCSV.fx" + ".SmartCSV.fx" +
File.separator + "" + File.separator + "" +
"preferences.json"); "preferences.json");
public static final String CSV_FILTER_TEXT = "CSV files (*.csv)";
public static final String CSV_FILTER_EXTENSION = "*.csv";
public static final String JSON_FILTER_TEXT = "JSON files (*.json)";
public static final String JSON_FILTER_EXTENSION = "*.json";
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// injections // injections
@@ -108,12 +118,18 @@ public class SmartCSVController extends FXMLController {
@Autowired @Autowired
private CSVFileWriter csvFileWriter; private CSVFileWriter csvFileWriter;
@Autowired
private ValidationFileWriter validationFileWriter;
@Autowired @Autowired
private AboutController aboutController; private AboutController aboutController;
@Autowired @Autowired
private PreferencesController preferencesController; private PreferencesController preferencesController;
@Autowired
private ValidationEditorController validationEditorController;
@Autowired @Autowired
private LoadFileService loadFileService; private LoadFileService loadFileService;
@@ -141,6 +157,15 @@ public class SmartCSVController extends FXMLController {
@FXML @FXML
private MenuItem saveAsMenuItem; private MenuItem saveAsMenuItem;
@FXML
private MenuItem loadConfigMenuItem;
@FXML
private MenuItem saveConfigMenuItem;
@FXML
private MenuItem saveAsConfigMenuItem;
@FXML @FXML
private MenuItem deleteRowMenuItem; private MenuItem deleteRowMenuItem;
@@ -153,14 +178,21 @@ public class SmartCSVController extends FXMLController {
@FXML @FXML
private Button saveAsButton; private Button saveAsButton;
@FXML
private Button loadConfigButton;
@FXML
private Button saveConfigButton;
@FXML
private Button saveAsConfigButton;
@FXML @FXML
private Button deleteRowButton; private Button deleteRowButton;
@FXML @FXML
private Button addRowButton; private Button addRowButton;
private ErrorSideBar errorSideBar;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// members // members
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -169,11 +201,14 @@ public class SmartCSVController extends FXMLController {
private CSVModel model; private CSVModel model;
private TableView<CSVRow> tableView; private TableView<CSVRow> tableView;
private ErrorSideBar errorSideBar;
private BooleanProperty fileChanged = new SimpleBooleanProperty(true); private BooleanProperty fileChanged = new SimpleBooleanProperty(true);
private ResourceBundle resourceBundle; private ResourceBundle resourceBundle;
private ObjectProperty<File> currentCsvFile = new SimpleObjectProperty<>(); private ObjectProperty<File> currentCsvFile = new SimpleObjectProperty<>();
private ObjectProperty<File> currentConfigFile= new SimpleObjectProperty<>(); private ObjectProperty<File> currentConfigFile= new SimpleObjectProperty<>();
private ListChangeListener<ValidationError> errorListListener = c -> tableView.refresh();
private WeakListChangeListener<ValidationError> weakErrorListListener = new WeakListChangeListener<>(errorListListener);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// init // init
@@ -184,22 +219,28 @@ public class SmartCSVController extends FXMLController {
this.resourceBundle = resourceBundle; this.resourceBundle = resourceBundle;
setupTableCellFactory(); setupTableCellFactory();
setupErrorSideBar(resourceBundle);
errorSideBar = new ErrorSideBar(resourceBundle); bindMenuItemsToFileExistence(currentCsvFile, saveMenuItem, saveAsMenuItem, addRowMenuItem, loadConfigMenuItem);
bindButtonsToFileExistence(currentCsvFile, saveButton, saveAsButton, addRowButton, loadConfigButton);
errorSideBar.selectedValidationErrorProperty().addListener((observable, oldValue, newValue) -> { bindMenuItemsToFileExistence(currentConfigFile, saveConfigMenuItem, saveAsConfigMenuItem);
scrollToError(newValue); bindButtonsToFileExistence(currentConfigFile, saveAsConfigButton, saveConfigButton);
});
applicationPane.setRight(errorSideBar);
bindMenuItemsToCsvFileExtistence(saveMenuItem, saveAsMenuItem, addRowMenuItem);
bindButtonsToCsvFileExistence(saveButton, saveAsButton, addRowButton);
bindCsvFileName(); bindCsvFileName();
bindConfigFileName(); bindConfigFileName();
loadCsvPreferencesFromFile(); loadCsvPreferencesFromFile();
} }
private void setupErrorSideBar(ResourceBundle resourceBundle) {
errorSideBar = new ErrorSideBar(resourceBundle);
errorSideBar.selectedValidationErrorProperty().addListener((observable, oldValue, newValue) -> {
scrollToError(newValue);
});
applicationPane.setRight(errorSideBar);
}
private void setupTableCellFactory() { private void setupTableCellFactory() {
cellFactory = new ValidationCellFactory(resourceBundle); cellFactory = new ValidationCellFactory(resourceBundle);
} }
@@ -225,8 +266,8 @@ public class SmartCSVController extends FXMLController {
currentCsvFile.setValue( currentCsvFile.setValue(
loadFile( loadFile(
csvLoader, csvLoader,
"CSV files (*.csv)", CSV_FILTER_TEXT,
"*.csv", CSV_FILTER_EXTENSION,
"Open CSV", "Open CSV",
currentCsvFile.getValue())); currentCsvFile.getValue()));
} }
@@ -236,8 +277,8 @@ public class SmartCSVController extends FXMLController {
currentConfigFile.setValue( currentConfigFile.setValue(
loadFile( loadFile(
validationLoader, validationLoader,
"JSON files (*.json)", JSON_FILTER_TEXT,
"*.json", JSON_FILTER_EXTENSION,
"Open Validation Configuration", "Open Validation Configuration",
currentConfigFile.getValue())); currentConfigFile.getValue()));
} }
@@ -254,11 +295,28 @@ public class SmartCSVController extends FXMLController {
currentCsvFile.setValue( currentCsvFile.setValue(
saveFile( saveFile(
csvFileWriter, csvFileWriter,
"CSV files (*.csv)", CSV_FILTER_TEXT,
"*.csv", CSV_FILTER_EXTENSION,
currentCsvFile.getValue())); currentCsvFile.getValue()));
} }
@FXML
public void saveConfig(ActionEvent actionEvent) {
validationFileWriter.setValidationConfiguration(validationLoader.getValidationConfiguration());
useSaveFileService(validationFileWriter, currentConfigFile.getValue());
}
@FXML
public void saveAsConfig(ActionEvent actionEvent) {
validationFileWriter.setValidationConfiguration(validationLoader.getValidationConfiguration());
currentConfigFile.setValue(
saveFile(
validationFileWriter,
JSON_FILTER_TEXT,
JSON_FILTER_EXTENSION,
currentConfigFile.getValue()));
}
@FXML @FXML
public void close(ActionEvent actionEvent) { public void close(ActionEvent actionEvent) {
if (canExit()) { if (canExit()) {
@@ -333,6 +391,25 @@ public class SmartCSVController extends FXMLController {
return canExit; return canExit;
} }
public void showValidationEditor(String column) {
validationEditorController.setSelectedColumn(column);
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setGraphic(null);
alert.setTitle(resourceBundle.getString("dialog.validation.rules.title"));
alert.setHeaderText(format(resourceBundle.getString("dialog.validation.rules.header"), column));
alert.getDialogPane().setContent(validationEditorController.getView());
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
runLater(() -> {
validationEditorController.updateConfiguration();
fileChanged.setValue(true);
model.revalidate();
});
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// private methods // private methods
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -344,15 +421,15 @@ public class SmartCSVController extends FXMLController {
tableView.getSelectionModel().select(lastRow); tableView.getSelectionModel().select(lastRow);
} }
private void bindMenuItemsToCsvFileExtistence(MenuItem... items) { private void bindMenuItemsToFileExistence(ObjectProperty<File> file, MenuItem... items) {
for (MenuItem item: items) { for (MenuItem item: items) {
item.disableProperty().bind(isNull(currentCsvFile)); item.disableProperty().bind(isNull(file));
} }
} }
private void bindButtonsToCsvFileExistence(Button... items) { private void bindButtonsToFileExistence(ObjectProperty<File> file, Button... items) {
for (Button item: items) { for (Button item: items) {
item.disableProperty().bind(isNull(currentCsvFile)); item.disableProperty().bind(isNull(file));
} }
} }
@@ -378,9 +455,9 @@ public class SmartCSVController extends FXMLController {
private void loadCsvPreferencesFromFile() { private void loadCsvPreferencesFromFile() {
if (PREFERENCES_FILE.exists()) { if (PREFERENCES_FILE.exists()) {
useLoadFileService(preferencesLoader, PREFERENCES_FILE); useLoadFileService(preferencesLoader, PREFERENCES_FILE,
event -> setCsvPreference(preferencesLoader.getCSVpreference()));
} }
setCsvPreference(preferencesLoader.getCSVpreference());
} }
private void saveCsvPreferences(CsvPreference csvPreference) { private void saveCsvPreferences(CsvPreference csvPreference) {
@@ -431,7 +508,10 @@ public class SmartCSVController extends FXMLController {
//Show open file dialog //Show open file dialog
File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow()); File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow());
if (file != null) { if (file != null) {
useLoadFileService(fileReader, file); useLoadFileService(fileReader, file, event -> runLater(() -> {
resetContent();
fileChanged.setValue(false);
}));
return file; return file;
} else { } else {
return initChildFile; return initChildFile;
@@ -462,14 +542,11 @@ public class SmartCSVController extends FXMLController {
return file; return file;
} }
private void useLoadFileService(FileReader fileReader, File file) { private void useLoadFileService(FileReader fileReader, File file, EventHandler<WorkerStateEvent> value) {
loadFileService.setFile(file); loadFileService.setFile(file);
loadFileService.setFileReader(fileReader); loadFileService.setFileReader(fileReader);
loadFileService.restart(); loadFileService.restart();
loadFileService.setOnSucceeded(event -> runLater(() -> { loadFileService.setOnSucceeded(value);
resetContent();
fileChanged.setValue(false);
}));
} }
private void useSaveFileService(FileWriter writer, File file) { private void useSaveFileService(FileWriter writer, File file) {
@@ -488,7 +565,9 @@ public class SmartCSVController extends FXMLController {
private void resetContent() { private void resetContent() {
model = csvLoader.getData(); model = csvLoader.getData();
if (model != null) { if (model != null) {
model.setValidator(validationLoader.getValidator()); model.getValidationError().addListener(weakErrorListListener);
model.setValidationConfiguration(validationLoader.getValidationConfiguration());
validationEditorController.setValidationConfiguration(validationLoader.getValidationConfiguration());
tableView = new TableView<>(); tableView = new TableView<>();
bindMenuItemsToTableSelection(deleteRowMenuItem); bindMenuItemsToTableSelection(deleteRowMenuItem);
@@ -520,6 +599,11 @@ public class SmartCSVController extends FXMLController {
column.setCellValueFactory(new ObservableMapValueFactory(header)); column.setCellValueFactory(new ObservableMapValueFactory(header));
column.setCellFactory(cellFactory); column.setCellFactory(cellFactory);
column.setEditable(true); column.setEditable(true);
column.setSortable(false);
ContextMenu contextMenu = contextMenuForColumn(header);
column.setContextMenu(contextMenu);
column.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<CSVRow, CSVValue>>() { column.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<CSVRow, CSVValue>>() {
@Override @Override
public void handle(TableColumn.CellEditEvent<CSVRow, CSVValue> event) { public void handle(TableColumn.CellEditEvent<CSVRow, CSVValue> event) {
@@ -535,6 +619,15 @@ public class SmartCSVController extends FXMLController {
tableView.getColumns().add(column); tableView.getColumns().add(column);
} }
private ContextMenu contextMenuForColumn(String header) {
ContextMenu contextMenu = new ContextMenu();
MenuItem editColumnRulesMenuItem = new MenuItem(resourceBundle.getString("context.menu.edit.column.rules"));
bindMenuItemsToFileExistence(currentConfigFile, editColumnRulesMenuItem);
editColumnRulesMenuItem.setOnAction(e -> showValidationEditor(header));
contextMenu.getItems().addAll(editColumnRulesMenuItem);
return contextMenu;
}
private void scrollToError(ValidationError entry) { private void scrollToError(ValidationError entry) {
if (entry != null) { if (entry != null) {
if (entry.getLineNumber() != null) { if (entry.getLineNumber() != null) {

View File

@@ -26,12 +26,8 @@
package ninja.javafx.smartcsv.fx.preferences; package ninja.javafx.smartcsv.fx.preferences;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;

View File

@@ -28,6 +28,7 @@ package ninja.javafx.smartcsv.fx.table.model;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
import ninja.javafx.smartcsv.validation.ValidationError; import ninja.javafx.smartcsv.validation.ValidationError;
import ninja.javafx.smartcsv.validation.Validator; import ninja.javafx.smartcsv.validation.Validator;
@@ -43,11 +44,11 @@ public class CSVModel {
private ObservableList<ValidationError> validationError = FXCollections.observableArrayList(); private ObservableList<ValidationError> validationError = FXCollections.observableArrayList();
/** /**
* sets the validator for the data revalidates * sets the validator configuration for the data revalidates
* @param validator the validator for this data * @param validationConfiguration the validator configuration for this data
*/ */
public void setValidator(Validator validator) { public void setValidationConfiguration(ValidationConfiguration validationConfiguration) {
this.validator = validator; this.validator = new Validator(validationConfiguration);
revalidate(); revalidate();
} }

View File

@@ -0,0 +1,348 @@
/*
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.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
/**
* controller for editing column validations
*/
@Component
public class ValidationEditorController extends FXMLController {
private StringProperty selectedColumn = new SimpleStringProperty();
private ValidationConfiguration validationConfiguration;
@FXML
private CheckBox notEmptyRuleCheckBox;
@FXML
private CheckBox integerRuleCheckBox;
@FXML
private CheckBox doublerRuleCheckBox;
@FXML
private CheckBox alphanumericRuleCheckBox;
@FXML
private Spinner<Integer> minLengthSpinner;
@FXML
private Spinner<Integer> maxLengthSpinner;
@FXML
private TextField dateformatRuleTextField;
@FXML
private TextField regexpRuleTextField;
@FXML
private TextField valueOfRuleTextField;
@FXML
private TextArea groovyRuleTextArea;
@FXML
private CheckBox enableNotEmptyRule;
@FXML
private CheckBox enableIntegerRule;
@FXML
private CheckBox enableDoubleRule;
@FXML
private CheckBox enableAlphanumericRule;
@FXML
private CheckBox enableMinLengthRule;
@FXML
private CheckBox enableMaxLengthRule;
@FXML
private CheckBox enableDateRule;
@FXML
private CheckBox enableRegexpRule;
@FXML
private CheckBox enableValueOfRule;
@FXML
private CheckBox enableGroovyRule;
@Value("${fxml.smartcvs.validation.editor.view}")
@Override
public void setFxmlFilePath(String filePath) {
this.fxmlFilePath = filePath;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
minLengthSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, 0));
maxLengthSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, 0));
initCheckBox(notEmptyRuleCheckBox, enableNotEmptyRule);
initCheckBox(integerRuleCheckBox, enableIntegerRule);
initCheckBox(doublerRuleCheckBox, enableDoubleRule);
initCheckBox(alphanumericRuleCheckBox, enableAlphanumericRule);
initSpinner(minLengthSpinner, enableMinLengthRule);
initSpinner(maxLengthSpinner, enableMaxLengthRule);
initTextInputControl(dateformatRuleTextField, enableDateRule);
initTextInputControl(regexpRuleTextField, enableRegexpRule);
initTextInputControl(valueOfRuleTextField, enableValueOfRule);
initTextInputControl(groovyRuleTextArea, enableGroovyRule);
selectedColumn.addListener(observable -> {
updateForm();
});
}
public String getSelectedColumn() {
return selectedColumn.get();
}
public StringProperty selectedColumnProperty() {
return selectedColumn;
}
public void setSelectedColumn(String selectedColumn) {
this.selectedColumn.set(selectedColumn);
}
public void setValidationConfiguration(ValidationConfiguration validationConfiguration) {
this.validationConfiguration = validationConfiguration;
}
public void updateConfiguration() {
if (enableIntegerRule.isSelected()) {
validationConfiguration.setIntegerRuleFor(selectedColumn.getValue(), integerRuleCheckBox.isSelected());
} else {
validationConfiguration.setIntegerRuleFor(selectedColumn.getValue(), null);
}
if (enableNotEmptyRule.isSelected()) {
validationConfiguration.setNotEmptyRuleFor(selectedColumn.getValue(), notEmptyRuleCheckBox.isSelected());
} else {
validationConfiguration.setNotEmptyRuleFor(selectedColumn.getValue(), null);
}
if (enableDoubleRule.isSelected()) {
validationConfiguration.setDoubleRuleFor(selectedColumn.getValue(), doublerRuleCheckBox.isSelected());
} else {
validationConfiguration.setDoubleRuleFor(selectedColumn.getValue(), null);
}
if (enableAlphanumericRule.isSelected()) {
validationConfiguration.setAlphanumericRuleFor(selectedColumn.getValue(), alphanumericRuleCheckBox.isSelected());
} else {
validationConfiguration.setAlphanumericRuleFor(selectedColumn.getValue(), null);
}
if (enableDateRule.isSelected()) {
validationConfiguration.setDateRuleFor(selectedColumn.getValue(), dateformatRuleTextField.getText());
} else {
validationConfiguration.setDateRuleFor(selectedColumn.getValue(), null);
}
if (enableGroovyRule.isSelected()) {
validationConfiguration.setGroovyRuleFor(selectedColumn.getValue(), groovyRuleTextArea.getText());
} else {
validationConfiguration.setGroovyRuleFor(selectedColumn.getValue(), null);
}
if (enableMinLengthRule.isSelected()) {
validationConfiguration.setMinLengthRuleFor(selectedColumn.getValue(), minLengthSpinner.getValue());
} else {
validationConfiguration.setMinLengthRuleFor(selectedColumn.getValue(), null);
}
if (enableMaxLengthRule.isSelected()) {
validationConfiguration.setMaxLengthRuleFor(selectedColumn.getValue(), maxLengthSpinner.getValue());
} else {
validationConfiguration.setMaxLengthRuleFor(selectedColumn.getValue(), null);
}
if (enableRegexpRule.isSelected()) {
validationConfiguration.setRegexpRuleFor(selectedColumn.getValue(), regexpRuleTextField.getText());
} else {
validationConfiguration.setRegexpRuleFor(selectedColumn.getValue(), null);
}
if (enableValueOfRule.isSelected()) {
validationConfiguration.setValueOfRuleFor(selectedColumn.getValue(), asList(valueOfRuleTextField.getText().split(", ")));
} else {
validationConfiguration.setValueOfRuleFor(selectedColumn.getValue(), null);
}
}
private void updateForm() {
updateCheckBox(
notEmptyRuleCheckBox,
validationConfiguration.getNotEmptyRuleFor(getSelectedColumn()),
enableNotEmptyRule
);
updateCheckBox(
integerRuleCheckBox,
validationConfiguration.getIntegerRuleFor(getSelectedColumn()),
enableIntegerRule
);
updateCheckBox(
doublerRuleCheckBox,
validationConfiguration.getDoubleRuleFor(getSelectedColumn()),
enableDoubleRule
);
updateCheckBox(
alphanumericRuleCheckBox,
validationConfiguration.getAlphanumericRuleFor(getSelectedColumn()),
enableAlphanumericRule
);
updateSpinner(
minLengthSpinner,
validationConfiguration.getMinLengthRuleFor(getSelectedColumn()),
enableMinLengthRule
);
updateSpinner(
maxLengthSpinner,
validationConfiguration.getMaxLengthRuleFor(getSelectedColumn()),
enableMaxLengthRule
);
updateTextInputControl(
dateformatRuleTextField,
validationConfiguration.getDateRuleFor(getSelectedColumn()),
enableDateRule
);
updateTextInputControl(
regexpRuleTextField,
validationConfiguration.getRegexpRuleFor(getSelectedColumn()),
enableRegexpRule
);
updateTextInputControl(
valueOfRuleTextField,
validationConfiguration.getValueOfRuleFor(getSelectedColumn()),
enableValueOfRule
);
updateTextInputControl(
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 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("");
}
});
}
}

View File

@@ -26,8 +26,7 @@
package ninja.javafx.smartcsv.preferences; package ninja.javafx.smartcsv.preferences;
import com.typesafe.config.Config; import com.google.gson.GsonBuilder;
import com.typesafe.config.ConfigFactory;
import ninja.javafx.smartcsv.FileReader; import ninja.javafx.smartcsv.FileReader;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.supercsv.prefs.CsvPreference; import org.supercsv.prefs.CsvPreference;
@@ -35,6 +34,8 @@ import org.supercsv.quote.AlwaysQuoteMode;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static ninja.javafx.smartcsv.preferences.QuoteModeHelper.getQuoteMode; import static ninja.javafx.smartcsv.preferences.QuoteModeHelper.getQuoteMode;
@@ -44,7 +45,7 @@ import static ninja.javafx.smartcsv.preferences.QuoteModeHelper.getQuoteMode;
@Service @Service
public class PreferencesFileReader implements FileReader { public class PreferencesFileReader implements FileReader {
private Config config; private Map config;
private CsvPreference csvPreference; private CsvPreference csvPreference;
public PreferencesFileReader() { public PreferencesFileReader() {
@@ -55,15 +56,15 @@ public class PreferencesFileReader implements FileReader {
@Override @Override
public void read(File filename) throws IOException { public void read(File filename) throws IOException {
config = ConfigFactory.parseFile(filename); config = new GsonBuilder().create().fromJson(new java.io.FileReader(filename), HashMap.class);
if (config != null) { if (config != null) {
char quoteChar = config.getString("quoteChar").charAt(0); char quoteChar = config.get("quoteChar").toString().charAt(0);
char delimiterChar = config.getString("delimiterChar").charAt(0); char delimiterChar = config.get("delimiterChar").toString().charAt(0);
String endOfLineSymbols = config.getString("endOfLineSymbols"); String endOfLineSymbols = config.get("endOfLineSymbols").toString();
boolean surroundingSpacesNeedQuotes = config.getBoolean("surroundingSpacesNeedQuotes"); boolean surroundingSpacesNeedQuotes = (Boolean)config.get("surroundingSpacesNeedQuotes");
boolean ignoreEmptyLines = config.getBoolean("ignoreEmptyLines"); boolean ignoreEmptyLines = (Boolean)config.get("ignoreEmptyLines");
String quoteMode = config.getString("quoteMode"); String quoteMode = config.get("quoteMode").toString();
csvPreference = new CsvPreference.Builder(quoteChar, delimiterChar, endOfLineSymbols) csvPreference = new CsvPreference.Builder(quoteChar, delimiterChar, endOfLineSymbols)
.useQuoteMode(getQuoteMode(quoteMode)) .useQuoteMode(getQuoteMode(quoteMode))

View File

@@ -26,9 +26,8 @@
package ninja.javafx.smartcsv.preferences; package ninja.javafx.smartcsv.preferences;
import com.typesafe.config.Config; import com.google.gson.Gson;
import com.typesafe.config.ConfigFactory; import com.google.gson.GsonBuilder;
import com.typesafe.config.ConfigRenderOptions;
import ninja.javafx.smartcsv.FileWriter; import ninja.javafx.smartcsv.FileWriter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.supercsv.prefs.CsvPreference; import org.supercsv.prefs.CsvPreference;
@@ -60,8 +59,8 @@ public class PreferencesFileWriter implements FileWriter {
preferences.put("ignoreEmptyLines", csvPreference.isIgnoreEmptyLines()); preferences.put("ignoreEmptyLines", csvPreference.isIgnoreEmptyLines());
preferences.put("quoteMode", QuoteModeHelper.getQuoteModeName(csvPreference.getQuoteMode())); preferences.put("quoteMode", QuoteModeHelper.getQuoteModeName(csvPreference.getQuoteMode()));
Config config = ConfigFactory.parseMap(preferences); Gson gson = new GsonBuilder().setPrettyPrinting().create();
Files.write(file.toPath(), config.root().render(ConfigRenderOptions.concise()).getBytes()); Files.write(file.toPath(), gson.toJson(preferences).getBytes());
} }
} }

View File

@@ -0,0 +1,42 @@
/*
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;
}
}

View File

@@ -0,0 +1,169 @@
/*
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;
import static java.lang.Boolean.FALSE;
/**
* 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 Boolean getIntegerRuleFor(String column) {
return (Boolean)getValue(column, "integer");
}
public Boolean getDoubleRuleFor(String column) {
return (Boolean)getValue(column, "double");
}
public Boolean getNotEmptyRuleFor(String column) {
return (Boolean)getValue(column, "not empty");
}
public Integer getMinLengthRuleFor(String column) {
return doubleToInteger((Double)getValue(column, "minlength"));
}
public Integer getMaxLengthRuleFor(String column) {
if (noRulesFor(column)) return null;
return doubleToInteger((Double)getValue(column, "maxlength"));
}
public String getDateRuleFor(String column) {
if (noRulesFor(column)) return null;
return (String)getValue(column, "date");
}
public Boolean getAlphanumericRuleFor(String column) {
if (noRulesFor(column)) return null;
return (Boolean)getValue(column, "alphanumeric");
}
public String getRegexpRuleFor(String column) {
if (noRulesFor(column)) return null;
return (String)getValue(column, "regexp");
}
public List<String> getValueOfRuleFor(String column) {
if (noRulesFor(column)) return null;
return (List<String>)getValue(column, "value of");
}
public String getGroovyRuleFor(String column) {
if (noRulesFor(column)) return null;
return (String)getValue(column, "groovy");
}
public void setIntegerRuleFor(String column, Boolean value) {
setValue(column, value, "integer");
}
public void setDoubleRuleFor(String column, Boolean value) {
setValue(column, value, "double");
}
public void setNotEmptyRuleFor(String column, Boolean value) {
setValue(column, value, "not empty");
}
public void setMinLengthRuleFor(String column, Integer value) {
setValue(column, value == null ? null : value.doubleValue(), "minlength");
}
public void setMaxLengthRuleFor(String column, Integer value) {
setValue(column, value == null ? null : value.doubleValue(), "maxlength");
}
public void setDateRuleFor(String column, String value) {
setValue(column, value, "date");
}
public void setAlphanumericRuleFor(String column, Boolean value) {
setValue(column, value, "alphanumeric");
}
public void setRegexpRuleFor(String column, String value) {
setValue(column, value, "regexp");
}
public void setValueOfRuleFor(String column, List<String> value) {
setValue(column, value, "value of");
}
public void setGroovyRuleFor(String column, String value) {
setValue(column, value, "groovy");
}
private void setValue(String column, Object value, String key) {
if (!columnConfigurations.containsKey(column)) {
columnConfigurations.put(column, new HashMap<>());
}
if (value == null && columnConfigurations.get(column).containsKey(key)) {
columnConfigurations.get(column).remove(key);
} else {
columnConfigurations.get(column).put(key, value);
}
}
private Object getValue(String column, String key) {
if (noRulesFor(column)) return null;
return columnConfigurations.get(column).get(key);
}
private boolean noHeader() {
return headerConfiguration == null;
}
private boolean noRulesFor(String column) {
return columnConfigurations == null || columnConfigurations.get(column) == null;
}
private Integer doubleToInteger(Double value) {
if (value == null) return null;
return (int)Math.round(value);
}
}

View File

@@ -26,8 +26,7 @@
package ninja.javafx.smartcsv.validation; package ninja.javafx.smartcsv.validation;
import com.typesafe.config.Config; import com.google.gson.GsonBuilder;
import com.typesafe.config.ConfigFactory;
import ninja.javafx.smartcsv.FileReader; import ninja.javafx.smartcsv.FileReader;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -40,14 +39,14 @@ import java.io.IOException;
@Service @Service
public class ValidationFileReader implements FileReader { public class ValidationFileReader implements FileReader {
private Config config; private ValidationConfiguration config;
@Override @Override
public void read(File file) throws IOException { public void read(File file) throws IOException {
config = ConfigFactory.parseFile(file); config = new GsonBuilder().create().fromJson(new java.io.FileReader(file), ValidationConfiguration.class);
} }
public Validator getValidator() { public ValidationConfiguration getValidationConfiguration() {
return new Validator(config); return config;
} }
} }

View File

@@ -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 com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import ninja.javafx.smartcsv.FileWriter;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
/**
* file writer for the validation configuration
*/
@Service
public class ValidationFileWriter implements FileWriter {
private ValidationConfiguration validationConfiguration;
public void setValidationConfiguration(ValidationConfiguration validationConfiguration) {
this.validationConfiguration = validationConfiguration;
}
@Override
public void write(File file) throws IOException {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Files.write(file.toPath(), gson.toJson(validationConfiguration).getBytes());
}
}

View File

@@ -26,7 +26,6 @@
package ninja.javafx.smartcsv.validation; package ninja.javafx.smartcsv.validation;
import com.typesafe.config.Config;
import groovy.lang.Binding; import groovy.lang.Binding;
import groovy.lang.GroovyShell; import groovy.lang.GroovyShell;
import groovy.lang.Script; import groovy.lang.Script;
@@ -49,7 +48,7 @@ public class Validator {
// member variables // member variables
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private Config validationConfig; private ValidationConfiguration validationConfig;
private GroovyShell shell = new GroovyShell(); private GroovyShell shell = new GroovyShell();
private Map<String, Script> scriptCache = new HashMap<>(); private Map<String, Script> scriptCache = new HashMap<>();
@@ -62,7 +61,7 @@ public class Validator {
* JSON configuration for this validator * JSON configuration for this validator
* @param validationConfig * @param validationConfig
*/ */
public Validator(Config validationConfig) { public Validator(ValidationConfiguration validationConfig) {
this.validationConfig = validationConfig; this.validationConfig = validationConfig;
} }
@@ -80,29 +79,23 @@ public class Validator {
public ValidationError isValid(String column, String value, Integer lineNumber) { public ValidationError isValid(String column, String value, Integer lineNumber) {
ValidationError result = null; ValidationError result = null;
if (validationConfig != null) { if (validationConfig != null) {
Config columnSectionConfig = getColumnSectionConfig();
if (columnSectionConfig != null) {
Config columnConfig = getColumnConfig(columnSectionConfig, column);
if (columnConfig != null) {
ValidationError error = ValidationError.withLineNumber(lineNumber); ValidationError error = ValidationError.withLineNumber(lineNumber);
checkBlankOrNull(columnConfig, value, error); checkBlankOrNull(column, value, error);
if (value != null && !value.isEmpty()) { if (value != null && !value.isEmpty()) {
checkRegularExpression(columnConfig, value, error); checkRegularExpression(column, value, error);
checkAlphaNumeric(columnConfig, value, error); checkAlphaNumeric(column, value, error);
checkDate(columnConfig, value, error); checkDate(column, value, error);
checkMaxLength(columnConfig, value, error); checkMaxLength(column, value, error);
checkMinLength(columnConfig, value, error); checkMinLength(column, value, error);
checkInteger(columnConfig, value, error); checkInteger(column, value, error);
checkGroovy(column, columnConfig, value, error); checkGroovy(column, value, error);
checkValueOf(columnConfig, value, error); checkValueOf(column, value, error);
checkDouble(columnConfig, value, error); checkDouble(column, value, error);
} }
if (!error.isEmpty()) { if (!error.isEmpty()) {
result = error; result = error;
}
}
} }
} }
return result; return result;
@@ -113,8 +106,8 @@ public class Validator {
// private methods // private methods
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void checkGroovy(String column, Config columnConfig, String value, ValidationError error) { private void checkGroovy(String column, String value, ValidationError error) {
String groovyScript = getString(columnConfig, "groovy"); String groovyScript = validationConfig.getGroovyRuleFor(column);
if (groovyScript != null) { if (groovyScript != null) {
Script script = scriptCache.get(column); Script script = scriptCache.get(column);
@@ -149,43 +142,42 @@ public class Validator {
return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true"); return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true");
} }
private void checkValueOf(Config columnConfig, String value, ValidationError error) { private void checkValueOf(String column, String value, ValidationError error) {
List<String> stringList = getStringList(columnConfig, "value of"); List<String> values = validationConfig.getValueOfRuleFor(column);
if (stringList != null) { if (values != null) {
if (!stringList.contains(value)) { if (!values.contains(value)) {
String commaSeparated = stringList.stream().collect(joining(", ")); String commaSeparated = values.stream().collect(joining(", "));
error.add("validation.message.value.of", value, commaSeparated); error.add("validation.message.value.of", value, commaSeparated);
} }
} }
} }
private void checkBlankOrNull(Config columnConfig, String value, ValidationError error) { private void checkBlankOrNull(String column, String value, ValidationError error) {
if (getBoolean(columnConfig, "not empty")) { if (validationConfig.getNotEmptyRuleFor(column) != null && validationConfig.getNotEmptyRuleFor(column)) {
if (isBlankOrNull(value)) { if (isBlankOrNull(value)) {
error.add("validation.message.not.empty"); error.add("validation.message.not.empty");
} }
} }
} }
private void checkInteger(Config columnConfig, String value, ValidationError error) { private void checkInteger(String column, String value, ValidationError error) {
if (getBoolean(columnConfig, "integer")) { if (validationConfig.getIntegerRuleFor(column) != null && validationConfig.getIntegerRuleFor(column)) {
if (!isInt(value)) { if (!isInt(value)) {
error.add("validation.message.integer"); error.add("validation.message.integer");
} }
} }
} }
private void checkDouble(Config columnConfig, String value, ValidationError error) { private void checkDouble(String column, String value, ValidationError error) {
if (getBoolean(columnConfig, "double")) { if (validationConfig.getDoubleRuleFor(column) != null && validationConfig.getDoubleRuleFor(column)) {
if (!isDouble(value)) { if (!isDouble(value)) {
error.add("validation.message.double"); error.add("validation.message.double");
} }
} }
} }
private void checkMinLength(String column, String value, ValidationError error) {
private void checkMinLength(Config columnConfig, String value, ValidationError error) { Integer minLength = validationConfig.getMinLengthRuleFor(column);
Integer minLength = getInteger(columnConfig, "minlength");
if (minLength != null) { if (minLength != null) {
if (!minLength(value, minLength)) { if (!minLength(value, minLength)) {
error.add("validation.message.min.length", minLength.toString()); error.add("validation.message.min.length", minLength.toString());
@@ -193,8 +185,8 @@ public class Validator {
} }
} }
private void checkMaxLength(Config columnConfig, String value, ValidationError error) { private void checkMaxLength(String column, String value, ValidationError error) {
Integer maxLength = getInteger(columnConfig, "maxlength"); Integer maxLength = validationConfig.getMaxLengthRuleFor(column);
if (maxLength != null) { if (maxLength != null) {
if (!maxLength(value, maxLength)) { if (!maxLength(value, maxLength)) {
error.add("validation.message.max.length", maxLength.toString()); error.add("validation.message.max.length", maxLength.toString());
@@ -202,8 +194,8 @@ public class Validator {
} }
} }
private void checkDate(Config columnConfig, String value, ValidationError error) { private void checkDate(String column, String value, ValidationError error) {
String dateformat = getString(columnConfig, "date"); String dateformat = validationConfig.getDateRuleFor(column);
if (dateformat != null && !dateformat.trim().isEmpty()) { if (dateformat != null && !dateformat.trim().isEmpty()) {
if (!isDate(value, dateformat, true)) { if (!isDate(value, dateformat, true)) {
error.add("validation.message.date.format", dateformat); error.add("validation.message.date.format", dateformat);
@@ -211,16 +203,16 @@ public class Validator {
} }
} }
private void checkAlphaNumeric(Config columnConfig, String value, ValidationError error) { private void checkAlphaNumeric(String column, String value, ValidationError error) {
if (getBoolean(columnConfig, "alphanumeric")) { if (validationConfig.getAlphanumericRuleFor(column) != null && validationConfig.getAlphanumericRuleFor(column)) {
if (!matchRegexp(value, "[0-9a-zA-Z]*")) { if (!matchRegexp(value, "[0-9a-zA-Z]*")) {
error.add("validation.message.alphanumeric"); error.add("validation.message.alphanumeric");
} }
} }
} }
private void checkRegularExpression(Config columnConfig, String value, ValidationError error) { private void checkRegularExpression(String column, String value, ValidationError error) {
String regexp = getString(columnConfig, "regexp"); String regexp = validationConfig.getRegexpRuleFor(column);
if (regexp != null && !regexp.trim().isEmpty()) { if (regexp != null && !regexp.trim().isEmpty()) {
if (!matchRegexp(value, regexp)) { if (!matchRegexp(value, regexp)) {
error.add("validation.message.regexp", regexp); error.add("validation.message.regexp", regexp);
@@ -228,60 +220,31 @@ public class Validator {
} }
} }
private Config getColumnSectionConfig() {
return validationConfig.hasPath("columns") ? validationConfig.getConfig("columns") : null;
}
private Config getColumnConfig(Config columnSectionConfig, String column) {
return columnSectionConfig.hasPath(column) ? columnSectionConfig.getConfig(column) : null;
}
private String getString(Config columnConfig, String path) {
return columnConfig.hasPath(path) ? columnConfig.getString(path) : null;
}
private Integer getInteger(Config columnConfig, String path) {
return columnConfig.hasPath(path) ? columnConfig.getInt(path) : null;
}
private boolean getBoolean(Config columnConfig, String path) {
return columnConfig.hasPath(path) && columnConfig.getBoolean(path);
}
private List<String> getStringList(Config columnConfig, String path) {
return columnConfig.hasPath(path) ? columnConfig.getStringList(path) : null;
}
public ValidationError isHeaderValid(String[] headerNames) { public ValidationError isHeaderValid(String[] headerNames) {
ValidationError result = null; ValidationError result = null;
if (validationConfig != null) { if (validationConfig != null) {
if (validationConfig.hasPath("headers")) { String[] headerNamesConfig = validationConfig.headerNames();
Config headerSectionConfig = validationConfig.getConfig("headers"); if (headerNamesConfig != null) {
List<String> headerConfig = getStringList(headerSectionConfig, "list"); if (headerNames.length != headerNamesConfig.length) {
if (headerConfig != null) { result = ValidationError.withoutLineNumber().add("validation.message.header.length",
if (headerNames.length != headerConfig.size()) { Integer.toString(headerNames.length),
result = ValidationError.withoutLineNumber().add("validation.message.header.length", Integer.toString(headerNamesConfig.length));
Integer.toString(headerNames.length), return result;
Integer.toString(headerConfig.size())); }
return result;
}
ValidationError error = ValidationError.withoutLineNumber(); ValidationError error = ValidationError.withoutLineNumber();
for(int i=0; i<headerConfig.size(); i++) { for(int i=0; i<headerNamesConfig.length; i++) {
String header = headerConfig.get(i); if (!headerNamesConfig[i].equals(headerNames[i])) {
if (!header.equals(headerNames[i])) { error.add("validation.message.header.match",
error.add("validation.message.header.match", Integer.toString(i),
Integer.toString(i), headerNamesConfig[i],
header, headerNames[i]);
headerNames[i]);
}
}
if (!error.isEmpty()) {
result = error;
} }
} }
if (!error.isEmpty()) {
result = error;
}
} }
} }
return result; return result;

View File

@@ -46,8 +46,8 @@
<Hyperlink onAction="#openLinkInBrowser" text="https://spring.io/" GridPane.columnIndex="1" GridPane.rowIndex="3" /> <Hyperlink onAction="#openLinkInBrowser" text="https://spring.io/" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Label text="supercsv" GridPane.rowIndex="4" /> <Label text="supercsv" GridPane.rowIndex="4" />
<Hyperlink onAction="#openLinkInBrowser" text="http://super-csv.github.io/super-csv/" GridPane.columnIndex="1" GridPane.rowIndex="4" /> <Hyperlink onAction="#openLinkInBrowser" text="http://super-csv.github.io/super-csv/" GridPane.columnIndex="1" GridPane.rowIndex="4" />
<Label text="config" GridPane.rowIndex="5" /> <Label text="Gson" GridPane.rowIndex="5" />
<Hyperlink onAction="#openLinkInBrowser" text="https://github.com/typesafehub/config" GridPane.columnIndex="1" GridPane.rowIndex="5" /> <Hyperlink onAction="#openLinkInBrowser" text="https://github.com/google/gson/" GridPane.columnIndex="1" GridPane.rowIndex="5" />
<Label text="commons-validator" GridPane.rowIndex="6" /> <Label text="commons-validator" GridPane.rowIndex="6" />
<Hyperlink onAction="#openLinkInBrowser" text="https://commons.apache.org/proper/commons-validator/" GridPane.columnIndex="1" GridPane.rowIndex="6" /> <Hyperlink onAction="#openLinkInBrowser" text="https://commons.apache.org/proper/commons-validator/" GridPane.columnIndex="1" GridPane.rowIndex="6" />
<Label text="fontawesomefx" GridPane.rowIndex="7" /> <Label text="fontawesomefx" GridPane.rowIndex="7" />

View File

@@ -5,6 +5,7 @@ application.version = 0.3
fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml
fxml.smartcvs.about.view = /ninja/javafx/smartcsv/fx/about/about.fxml fxml.smartcvs.about.view = /ninja/javafx/smartcsv/fx/about/about.fxml
fxml.smartcvs.preferences.view = /ninja/javafx/smartcsv/fx/preferences/preferences.fxml fxml.smartcvs.preferences.view = /ninja/javafx/smartcsv/fx/preferences/preferences.fxml
fxml.smartcvs.validation.editor.view = /ninja/javafx/smartcsv/fx/validation/validationEditor.fxml
# resources # resources
resource.main = ninja.javafx.smartcsv.fx.smartcsv resource.main = ninja.javafx.smartcsv.fx.smartcsv

View File

@@ -25,6 +25,7 @@
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?> <?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import ninja.javafx.smartcsv.fx.list.ErrorSideBar?> <?import ninja.javafx.smartcsv.fx.list.ErrorSideBar?>
<?import de.jensd.fx.glyphs.GlyphsStack?>
<BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="1000.0" minHeight="700.0" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"> <BorderPane fx:id="applicationPane" maxHeight="-Infinity" maxWidth="1000.0" minHeight="700.0" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<top> <top>
<VBox id="background" prefWidth="100.0" BorderPane.alignment="CENTER"> <VBox id="background" prefWidth="100.0" BorderPane.alignment="CENTER">
@@ -38,12 +39,6 @@
<FontAwesomeIconView styleClass="open-icon" /> <FontAwesomeIconView styleClass="open-icon" />
</graphic> </graphic>
</MenuItem> </MenuItem>
<MenuItem mnemonicParsing="false" onAction="#openConfig" text="%menu.open.config">
<graphic>
<FontAwesomeIconView styleClass="config-icon" />
</graphic>
</MenuItem>
<SeparatorMenuItem mnemonicParsing="false" />
<MenuItem fx:id="saveMenuItem" disable="true" mnemonicParsing="false" onAction="#saveCsv" text="%menu.save"> <MenuItem fx:id="saveMenuItem" disable="true" mnemonicParsing="false" onAction="#saveCsv" text="%menu.save">
<graphic> <graphic>
<FontAwesomeIconView styleClass="save-icon" /> <FontAwesomeIconView styleClass="save-icon" />
@@ -54,6 +49,34 @@
<FontAwesomeIconView styleClass="save-icon" /> <FontAwesomeIconView styleClass="save-icon" />
</graphic> </graphic>
</MenuItem> </MenuItem>
<SeparatorMenuItem mnemonicParsing="false" />
<MenuItem fx:id="loadConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#openConfig" text="%menu.open.config">
<graphic>
<FontAwesomeIconView styleClass="config-icon" />
</graphic>
</MenuItem>
<MenuItem fx:id="saveConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#saveConfig" text="%menu.save.config">
<graphic>
<GlyphsStack>
<children>
<FontAwesomeIconView styleClass="save-icon" style="-fx-opacity: 0.7;" />
<FontAwesomeIconView styleClass="config-icon" />
</children>
</GlyphsStack>
</graphic>
</MenuItem>
<MenuItem fx:id="saveAsConfigMenuItem" disable="true" mnemonicParsing="false" onAction="#saveAsConfig" text="%menu.save.as.config">
<graphic>
<GlyphsStack>
<children>
<FontAwesomeIconView styleClass="save-icon" style="-fx-opacity: 0.7;" />
<FontAwesomeIconView styleClass="config-icon" />
</children>
</GlyphsStack>
</graphic>
</MenuItem>
<SeparatorMenuItem mnemonicParsing="false" /> <SeparatorMenuItem mnemonicParsing="false" />
<MenuItem mnemonicParsing="false" onAction="#preferences" text="%menu.preferences"> <MenuItem mnemonicParsing="false" onAction="#preferences" text="%menu.preferences">
<graphic> <graphic>
@@ -103,14 +126,6 @@
<FontAwesomeIconView styleClass="open-icon" /> <FontAwesomeIconView styleClass="open-icon" />
</graphic> </graphic>
</Button> </Button>
<Button mnemonicParsing="false" onAction="#openConfig">
<tooltip>
<Tooltip text="%menu.open.config" />
</tooltip>
<graphic>
<FontAwesomeIconView styleClass="config-icon" />
</graphic>
</Button>
<Button fx:id="saveButton" disable="true" mnemonicParsing="false" onAction="#saveCsv"> <Button fx:id="saveButton" disable="true" mnemonicParsing="false" onAction="#saveCsv">
<tooltip> <tooltip>
<Tooltip text="%menu.save" /> <Tooltip text="%menu.save" />
@@ -128,6 +143,44 @@
</graphic> </graphic>
</Button> </Button>
</HBox> </HBox>
<Region styleClass="spacer" />
<HBox styleClass="segmented-button-bar">
<Button fx:id="loadConfigButton" mnemonicParsing="false" disable="true" onAction="#openConfig">
<tooltip>
<Tooltip text="%menu.open.config" />
</tooltip>
<graphic>
<FontAwesomeIconView styleClass="config-icon" />
</graphic>
</Button>
<Button fx:id="saveConfigButton" disable="true" mnemonicParsing="false" onAction="#saveConfig">
<tooltip>
<Tooltip text="%menu.save.config" />
</tooltip>
<graphic>
<GlyphsStack>
<children>
<FontAwesomeIconView styleClass="save-icon" style="-fx-opacity: 0.7;" />
<FontAwesomeIconView styleClass="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>
<FontAwesomeIconView styleClass="save-icon" style="-fx-opacity: 0.7;" />
<FontAwesomeIconView styleClass="config-icon" />
</children>
</GlyphsStack>
</graphic>
</Button>
</HBox>
<Region styleClass="spacer" /> <Region styleClass="spacer" />
<HBox styleClass="segmented-button-bar"> <HBox styleClass="segmented-button-bar">
<Button fx:id="deleteRowButton" disable="true" mnemonicParsing="false" onAction="#deleteRow" styleClass="first"> <Button fx:id="deleteRowButton" disable="true" mnemonicParsing="false" onAction="#deleteRow" styleClass="first">

View File

@@ -48,3 +48,19 @@ validation.message.regexp = does not match {0}
validation.message.header.length = number of headers is not correct! there are {0} but there should be {1} validation.message.header.length = number of headers is not correct! there are {0} but there should be {1}
validation.message.header.match = header number {0} does not match "{1}" should be "{3}" validation.message.header.match = header number {0} does not match "{1}" should be "{3}"
validation.message.value.of = Value {0} is not part of this list {1} validation.message.value.of = Value {0} is not part of this list {1}
validation.rule.label.not_empty = not empty:
validation.rule.label.integer = integer:
validation.rule.label.double = double:
validation.rule.label.minlength = minimum length:
validation.rule.label.maxlength = maximum length:
validation.rule.label.dateformat = date format:
validation.rule.label.alphanumeric = alphanumeric:
validation.rule.label.regexp = regular expression:
validation.rule.label.value_of = value in list:
validation.rule.label.groovy = groovy:
validation.rules.active = active
dialog.validation.rules.title = Validation rules
dialog.validation.rules.header = Validation rules of column "{0}"
context.menu.edit.column.rules = Edit rules

View File

@@ -10,6 +10,9 @@ menu.open.csv = CSV Datei \u00f6ffnen
menu.open.config = Pr\u00fcfkonfiguration \u00f6ffnen menu.open.config = Pr\u00fcfkonfiguration \u00f6ffnen
menu.save = Speichern menu.save = Speichern
menu.save.as = Speichern als ... menu.save.as = Speichern als ...
menu.save.config = Pr\u00fcfkonfiguration speichern
menu.save.as.config = Pr\u00fcfkonfiguration speichern als ...
menu.close = Beenden menu.close = Beenden
menu.about = \u00dcber ... menu.about = \u00dcber ...
menu.file = Datei menu.file = Datei
@@ -56,3 +59,20 @@ validation.message.regexp = entspricht nicht dem regul\u00e4ren Ausdruck {0}
validation.message.header.length = Anzahl der \u00dcberschriften ist nicht korrekt! Es sind {0} aber es sollten {1} sein validation.message.header.length = Anzahl der \u00dcberschriften ist nicht korrekt! Es sind {0} aber es sollten {1} sein
validation.message.header.match = \u00dcberschrift in Spalte {0} stimmt nicht. "{1}" sollte "{3}" sein validation.message.header.match = \u00dcberschrift in Spalte {0} stimmt nicht. "{1}" sollte "{3}" sein
validation.message.value.of = Der Wert {0} ist nicht in der Liste {1} enthalten validation.message.value.of = Der Wert {0} ist nicht in der Liste {1} enthalten
validation.rule.label.not_empty = Nicht leer:
validation.rule.label.integer = Zahl:
validation.rule.label.double = Gleitkommazahl:
validation.rule.label.minlength = Minimale L\u00e4nge:
validation.rule.label.maxlength = Maximale L\u00e4nge:
validation.rule.label.dateformat = Datumsformat:
validation.rule.label.alphanumeric = Nur Zahlen und Buchstaben:
validation.rule.label.regexp = Regul\u00e4rer Ausdruck:
validation.rule.label.value_of = In der Liste:
validation.rule.label.groovy = groovy:
validation.rules.active = Aktiv
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

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane hgap="10.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="600.0" vgap="10.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65">
<columnConstraints>
<ColumnConstraints hgrow="NEVER" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="NEVER" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="%validation.rule.label.not_empty" GridPane.rowIndex="1" />
<Label text="%validation.rule.label.integer" GridPane.rowIndex="2" />
<Label text="%validation.rule.label.double" GridPane.rowIndex="3" />
<Label text="%validation.rule.label.minlength" GridPane.rowIndex="5" />
<Label text="%validation.rule.label.maxlength" GridPane.rowIndex="6" />
<Label text="%validation.rule.label.dateformat" GridPane.rowIndex="7" />
<Label text="%validation.rule.label.alphanumeric" GridPane.rowIndex="4" />
<Label text="%validation.rule.label.regexp" GridPane.rowIndex="8" />
<Label text="%validation.rule.label.value_of" GridPane.rowIndex="9" />
<Label text="%validation.rule.label.groovy" GridPane.rowIndex="10" />
<CheckBox fx:id="notEmptyRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<CheckBox fx:id="integerRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<CheckBox fx:id="doublerRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<CheckBox fx:id="alphanumericRuleCheckBox" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="4" />
<Spinner fx:id="minLengthSpinner" GridPane.columnIndex="1" GridPane.rowIndex="5" />
<Spinner fx:id="maxLengthSpinner" GridPane.columnIndex="1" GridPane.rowIndex="6" />
<TextField fx:id="dateformatRuleTextField" GridPane.columnIndex="1" GridPane.rowIndex="7" />
<TextField fx:id="regexpRuleTextField" GridPane.columnIndex="1" GridPane.rowIndex="8" />
<TextField fx:id="valueOfRuleTextField" GridPane.columnIndex="1" GridPane.rowIndex="9" />
<TextArea fx:id="groovyRuleTextArea" prefHeight="300.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="10" GridPane.rowSpan="2" />
<CheckBox fx:id="enableNotEmptyRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<CheckBox fx:id="enableIntegerRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="2" />
<CheckBox fx:id="enableDoubleRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="3" />
<CheckBox fx:id="enableAlphanumericRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="4" />
<CheckBox fx:id="enableMinLengthRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="5" />
<CheckBox fx:id="enableMaxLengthRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="6" />
<CheckBox fx:id="enableDateRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="7" />
<CheckBox fx:id="enableRegexpRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="8" />
<CheckBox fx:id="enableValueOfRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="9" />
<CheckBox fx:id="enableGroovyRule" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="10" />
<Label text="%validation.rules.active" GridPane.columnIndex="2" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</GridPane>

View File

@@ -26,6 +26,7 @@
package ninja.javafx.smartcsv.fx.table.model; package ninja.javafx.smartcsv.fx.table.model;
import ninja.javafx.smartcsv.validation.ValidationConfiguration;
import ninja.javafx.smartcsv.validation.Validator; import ninja.javafx.smartcsv.validation.Validator;
import org.junit.Test; import org.junit.Test;
@@ -77,20 +78,6 @@ public class CSVModelTest {
assertThat(sut.getRows().indexOf(newRow), is(newRow.getRowNumber())); assertThat(sut.getRows().indexOf(newRow), is(newRow.getRowNumber()));
} }
@Test
public void set_validator_calls_when_model_has_data() {
// setup
Validator validator = mock(Validator.class);
setup_model_with_one_row_one_column_and_value();
// execution
sut.setValidator(validator);
// assertion
verify(validator).isValid(TESTHEADER, TESTVALUE, 0);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// private methods // private methods
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -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;
}
}

View File

@@ -26,20 +26,19 @@
package ninja.javafx.smartcsv.validation; package ninja.javafx.smartcsv.validation;
import com.typesafe.config.Config; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static ninja.javafx.smartcsv.validation.ConfigMock.headerSectionConfig; import static java.util.stream.Collectors.joining;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -52,7 +51,7 @@ public class HeaderValidationTest {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameters // parameters
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private Config config; private ValidationConfiguration config;
private Boolean expectedResult; private Boolean expectedResult;
private List<ValidationMessage> expectedErrors; private List<ValidationMessage> expectedErrors;
private String[] headerNames; private String[] headerNames;
@@ -66,11 +65,12 @@ public class HeaderValidationTest {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameterized constructor // parameterized constructor
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public HeaderValidationTest(String[] configHeaderNames, public HeaderValidationTest(String configHeaderNames,
String[] headerNames, String[] headerNames,
Boolean expectedResult, Boolean expectedResult,
List<ValidationMessage> expectedErrors) { List<ValidationMessage> expectedErrors) {
this.config = headerSectionConfig(configHeaderNames); Gson gson = new GsonBuilder().create();
this.config = gson.fromJson(configHeaderNames, ValidationConfiguration.class);
this.headerNames = headerNames; this.headerNames = headerNames;
this.expectedResult = expectedResult; this.expectedResult = expectedResult;
this.expectedErrors = expectedErrors; this.expectedErrors = expectedErrors;
@@ -106,11 +106,16 @@ public class HeaderValidationTest {
@Parameterized.Parameters @Parameterized.Parameters
public static Collection validationConfigurations() { public static Collection validationConfigurations() {
return asList(new Object[][] { return asList(new Object[][] {
{ new String[] {}, new String[] {}, true, null }, { json(), new String[] {}, true, null },
{ new String[] {"a"}, new String[] {"a"}, true, null }, { json("a"), new String[] {"a"}, true, null },
{ new String[] {"a"}, new String[] {"b"}, false, singletonList(new ValidationMessage("validation.message.header.match", "0", "a", "b"))}, { json("a"), new String[] {"b"}, false, singletonList(new ValidationMessage("validation.message.header.match", "0", "a", "b"))},
{ new String[] {"a"}, new String[] {"a","b"}, false, singletonList(new ValidationMessage("validation.message.header.length", "2", "1"))}, { json("a"), new String[] {"a","b"}, false, singletonList(new ValidationMessage("validation.message.header.length", "2", "1"))},
{ new String[] {"a", "b"}, new String[] {"b", "a"}, false, asList(new ValidationMessage("validation.message.header.match", "0", "a", "b"), new ValidationMessage("validation.message.header.match", "1", "b", "a")) } { json("a", "b"), new String[] {"b", "a"}, false, asList(new ValidationMessage("validation.message.header.match", "0", "a", "b"), new ValidationMessage("validation.message.header.match", "1", "b", "a")) }
}); });
} }
public static String json(String... headerNames) {
return "{\"headers\":{\"list\":[" + asList(headerNames).stream().collect(joining(", ")) + "]}}";
}
} }

View File

@@ -1,15 +1,17 @@
package ninja.javafx.smartcsv.validation; package ninja.javafx.smartcsv.validation;
import com.typesafe.config.Config; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static ninja.javafx.smartcsv.validation.ConfigMock.columnSectionConfig; import static java.util.stream.Collectors.joining;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@@ -23,7 +25,7 @@ public class ValidatorTest {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameters // parameters
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private Config config; private ValidationConfiguration config;
private String column; private String column;
private String value; private String value;
private Boolean expectedResult; private Boolean expectedResult;
@@ -39,14 +41,14 @@ public class ValidatorTest {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameterized constructor // parameterized constructor
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public ValidatorTest(String configcolumn, public ValidatorTest(String config,
String configValidation,
Object configValue,
String column, String column,
String value, String value,
Boolean expectedResult, Boolean expectedResult,
ValidationMessage expectedError) { ValidationMessage expectedError) {
this.config = columnSectionConfig(configcolumn, configValidation, configValue); System.out.println(config);
Gson gson = new GsonBuilder().create();
this.config = gson.fromJson(config, ValidationConfiguration.class);
this.column = column; this.column = column;
this.value = value; this.value = value;
this.expectedResult = expectedResult; this.expectedResult = expectedResult;
@@ -84,30 +86,42 @@ public class ValidatorTest {
@Parameterized.Parameters @Parameterized.Parameters
public static Collection validationConfigurations() { public static Collection validationConfigurations() {
return asList(new Object[][] { return asList(new Object[][] {
{ "column", "not empty", true, "column", "value", true, null }, { json("column", "not empty", true), "column", "value", true, null },
{ "column", "not empty", true, "column", "", false, new ValidationMessage("validation.message.not.empty") }, { json("column", "not empty", true), "column", "", false, new ValidationMessage("validation.message.not.empty") },
{ "column", "not empty", true, "column", null, false, new ValidationMessage("validation.message.not.empty") }, { json("column", "not empty", true), "column", null, false, new ValidationMessage("validation.message.not.empty") },
{ "column", "integer", true, "column", "999", true, null }, { json("column", "integer", true), "column", "999", true, null },
{ "column", "integer", true, "column", "a", false, new ValidationMessage("validation.message.integer") }, { json("column", "integer", true), "column", "a", false, new ValidationMessage("validation.message.integer") },
{ "column", "double", true, "column", "999", true, null }, { json("column", "double", true), "column", "999", true, null },
{ "column", "double", true, "column", "999.000", true, null }, { json("column", "double", true), "column", "999.000", true, null },
{ "column", "double", true, "column", "a", false, new ValidationMessage("validation.message.double") }, { json("column", "double", true), "column", "a", false, new ValidationMessage("validation.message.double") },
{ "column", "minlength", 2, "column", "12", true, null }, { json("column", "minlength", 2), "column", "12", true, null },
{ "column", "minlength", 2, "column", "1", false, new ValidationMessage("validation.message.min.length", "2") }, { json("column", "minlength", 2), "column", "1", false, new ValidationMessage("validation.message.min.length", "2") },
{ "column", "maxlength", 2, "column", "12", true, null }, { json("column", "maxlength", 2), "column", "12", true, null },
{ "column", "maxlength", 2, "column", "123", false, new ValidationMessage("validation.message.max.length", "2") }, { json("column", "maxlength", 2), "column", "123", false, new ValidationMessage("validation.message.max.length", "2") },
{ "column", "date", "yyyyMMdd", "column", "20151127", true, null }, { json("column", "date", "yyyyMMdd"), "column", "20151127", true, null },
{ "column", "date", "yyyyMMdd", "column", "27.11.2015", false, new ValidationMessage("validation.message.date.format", "yyyyMMdd") }, { json("column", "date", "yyyyMMdd"), "column", "27.11.2015", false, new ValidationMessage("validation.message.date.format", "yyyyMMdd") },
{ "column", "alphanumeric", true, "column", "abcABC123", true, null }, { json("column", "alphanumeric", true), "column", "abcABC123", true, null },
{ "column", "alphanumeric", true, "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") }, { json("column", "alphanumeric", true), "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") },
{ "column", "regexp", "[a-z]*", "column", "abc", true, null }, { json("column", "regexp", "[a-z]*"), "column", "abc", true, null },
{ "column", "regexp", "[a-z]*", "column", "abcA", false, new ValidationMessage("validation.message.regexp", "[a-z]*") }, { json("column", "regexp", "[a-z]*"), "column", "abcA", false, new ValidationMessage("validation.message.regexp", "[a-z]*") },
{ "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "abcdef", true, null }, { json("column", "groovy", "value.contains('a')? 'true' : 'no a inside'"), "column", "abcdef", true, null },
{ "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, new ValidationMessage("no a inside") }, { json("column", "groovy", "value.contains('a')? 'true' : 'no a inside'"), "column", "bcdefg", false, new ValidationMessage("no a inside") },
{ "column", "value of", asList("a","b","c","d","e"), "column", "c", true, null }, { json("column", "value of", asList("a","b","c","d","e")), "column", "c", true, null },
{ "column", "value of", asList("a","b","c","d","e"), "column", "f", false, new ValidationMessage("validation.message.value.of", "f", "a, b, c, d, e") }, { json("column", "value of", asList("a","b","c","d","e")), "column", "f", false, new ValidationMessage("validation.message.value.of", "f", "a, b, c, d, e") },
}); });
} }
public static String json(String column, String rule, Object value) {
String json = "{\"columns\":{\"" + column + "\":{\"" + rule + "\":";
if (value instanceof String) {
json += "\""+ value + "\"";
} else if (value instanceof List) {
List<String> list = (List<String>)value;
json += "[" + list.stream().collect(joining(", ")) + "]";
} else {
json += value;
}
json += "}}}";
return json;
}
} }