Merge remote-tracking branch 'remotes/origin/feature/header_validation'

This commit is contained in:
Andreas Billmann
2015-12-17 22:09:57 +01:00
20 changed files with 704 additions and 236 deletions

View File

@@ -16,6 +16,7 @@ repositories {
dependencies { dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.5' compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.5'
compile group: 'org.springframework', name:'spring-context', version: '4.2.3.RELEASE' compile group: 'org.springframework', name:'spring-context', version: '4.2.3.RELEASE'

View File

@@ -32,27 +32,26 @@ import javafx.concurrent.Task;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Alert; import javafx.scene.control.*;
import javafx.scene.control.Label; import javafx.scene.layout.AnchorPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import ninja.javafx.smartcsv.FileReader; import ninja.javafx.smartcsv.FileReader;
import ninja.javafx.smartcsv.csv.CSVFileReader; import ninja.javafx.smartcsv.csv.CSVFileReader;
import ninja.javafx.smartcsv.csv.CSVFileWriter; import ninja.javafx.smartcsv.csv.CSVFileWriter;
import ninja.javafx.smartcsv.fx.list.ValidationErrorListCell;
import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory; import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory;
import ninja.javafx.smartcsv.fx.table.ValidationCellFactory; import ninja.javafx.smartcsv.fx.table.ValidationCellFactory;
import ninja.javafx.smartcsv.fx.table.model.CSVModel; import ninja.javafx.smartcsv.fx.table.model.CSVModel;
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
import ninja.javafx.smartcsv.fx.table.model.CSVRow; import ninja.javafx.smartcsv.fx.table.model.CSVRow;
import ninja.javafx.smartcsv.fx.table.model.CSVValue;
import ninja.javafx.smartcsv.validation.ValidationError;
import ninja.javafx.smartcsv.validation.ValidationFileReader; import ninja.javafx.smartcsv.validation.ValidationFileReader;
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;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@@ -83,15 +82,22 @@ public class SmartCSVController extends FXMLController {
@FXML @FXML
private Label stateline; private Label stateline;
@FXML
private ListView errorList;
@FXML
private AnchorPane tableWrapper;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// injections // members
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private ValidationCellFactory cellFactory = new ValidationCellFactory(); private ValidationCellFactory cellFactory;
private final LoadCSVService loadCSVService = new LoadCSVService(); private final LoadCSVService loadCSVService = new LoadCSVService();
private final SaveCSVService saveCSVService = new SaveCSVService(); private final SaveCSVService saveCSVService = new SaveCSVService();
private CSVModel model; private CSVModel model;
private TableView<CSVRow> tableView;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -99,8 +105,13 @@ public class SmartCSVController extends FXMLController {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resourceBundle) {
cellFactory = new ValidationCellFactory(resourceBundle);
stateline.setVisible(false); stateline.setVisible(false);
errorList.setCellFactory(param -> new ValidationErrorListCell(resourceBundle));
errorList.getSelectionModel().selectedItemProperty().addListener(
observable -> scrollToError()
);
} }
@@ -215,7 +226,7 @@ public class SmartCSVController extends FXMLController {
model = csvLoader.getData(); model = csvLoader.getData();
model.setValidator(validationLoader.getValidator()); model.setValidator(validationLoader.getValidator());
TableView<CSVRow> tableView = new TableView<>(); tableView = new TableView<>();
for (String column: model.getHeader()) { for (String column: model.getHeader()) {
addColumn(column, tableView); addColumn(column, tableView);
@@ -223,7 +234,14 @@ public class SmartCSVController extends FXMLController {
tableView.getItems().setAll(model.getRows()); tableView.getItems().setAll(model.getRows());
tableView.setEditable(true); tableView.setEditable(true);
applicationPane.setCenter(tableView); AnchorPane.setBottomAnchor(tableView, 0.0);
AnchorPane.setTopAnchor(tableView, 0.0);
AnchorPane.setLeftAnchor(tableView, 0.0);
AnchorPane.setRightAnchor(tableView, 0.0);
tableWrapper.getChildren().setAll(tableView);
errorList.setItems(model.getValidationError());
} }
/** /**
@@ -248,6 +266,17 @@ public class SmartCSVController extends FXMLController {
tableView.getColumns().add(column); tableView.getColumns().add(column);
} }
private void scrollToError() {
ValidationError entry = (ValidationError)errorList.getSelectionModel().getSelectedItem();
if (entry != null) {
if (entry.getLineNumber() != null) {
tableView.scrollTo(entry.getLineNumber());
tableView.getSelectionModel().select(entry.getLineNumber());
} else {
tableView.scrollTo(0);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// inner class // inner class
@@ -277,7 +306,7 @@ public class SmartCSVController extends FXMLController {
try { try {
fileReader.read(file); fileReader.read(file);
runLater(SmartCSVController.this::resetContent); runLater(SmartCSVController.this::resetContent);
} catch (IOException ex) { } catch (Throwable ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
@@ -300,7 +329,7 @@ public class SmartCSVController extends FXMLController {
try { try {
csvFileWriter.saveFile(model); csvFileWriter.saveFile(model);
runLater(SmartCSVController.this::resetContent); runLater(SmartCSVController.this::resetContent);
} catch (IOException ex) { } catch (Throwable ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
return null; return null;

View File

@@ -0,0 +1,70 @@
/*
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.scene.control.ListCell;
import javafx.scene.text.Text;
import ninja.javafx.smartcsv.validation.ValidationError;
import java.util.ResourceBundle;
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
/**
* TODO: DESCRIPTION!!!
*/
public class ValidationErrorListCell extends ListCell<ValidationError> {
private ResourceBundle resourceBundle;
public ValidationErrorListCell(ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
}
@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);
}
}

View File

@@ -34,7 +34,10 @@ import javafx.scene.input.KeyCode;
import ninja.javafx.smartcsv.fx.table.model.CSVRow; import ninja.javafx.smartcsv.fx.table.model.CSVRow;
import ninja.javafx.smartcsv.fx.table.model.CSVValue; import ninja.javafx.smartcsv.fx.table.model.CSVValue;
import java.util.ResourceBundle;
import static javafx.application.Platform.runLater; import static javafx.application.Platform.runLater;
import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage;
/** /**
* Created by Andreas on 27.11.2015. * Created by Andreas on 27.11.2015.
@@ -42,6 +45,11 @@ import static javafx.application.Platform.runLater;
public class EditableValidationCell extends TableCell<CSVRow, CSVValue> { public class EditableValidationCell extends TableCell<CSVRow, CSVValue> {
private ValueTextField textField; private ValueTextField textField;
private ResourceBundle resourceBundle;
public EditableValidationCell(ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
}
@Override @Override
public void startEdit() { public void startEdit() {
@@ -64,12 +72,12 @@ public class EditableValidationCell extends TableCell<CSVRow, CSVValue> {
protected void updateItem(CSVValue item, boolean empty) { protected void updateItem(CSVValue item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item == null || item.getValid().isValid() || isEditing()) { if (item == null || item.getValidationError() == null || isEditing()) {
setStyle(""); setStyle("");
setTooltip(null); setTooltip(null);
} else if (!item.getValid().isValid()) { } else if (item.getValidationError() != null) {
setStyle("-fx-background-color: #ff8888"); setStyle("-fx-background-color: #ff8888");
setTooltip(new Tooltip(item.getValid().error())); setTooltip(new Tooltip(getI18nValidatioMessage(resourceBundle, item.getValidationError())));
} }
if (item == null || empty) { if (item == null || empty) {

View File

@@ -29,17 +29,24 @@ package ninja.javafx.smartcsv.fx.table;
import javafx.scene.control.TableCell; import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
import javafx.util.Callback; import javafx.util.Callback;
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
import ninja.javafx.smartcsv.fx.table.model.CSVRow; import ninja.javafx.smartcsv.fx.table.model.CSVRow;
import ninja.javafx.smartcsv.fx.table.model.CSVValue; import ninja.javafx.smartcsv.fx.table.model.CSVValue;
import java.util.ResourceBundle;
/** /**
* Created by Andreas on 18.11.2015. * Created by Andreas on 18.11.2015.
*/ */
public class ValidationCellFactory implements Callback<TableColumn<CSVRow, CSVValue>, TableCell<CSVRow, CSVValue>> { public class ValidationCellFactory implements Callback<TableColumn<CSVRow, CSVValue>, TableCell<CSVRow, CSVValue>> {
private ResourceBundle resourceBundle;
public ValidationCellFactory(ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
}
@Override @Override
public TableCell<CSVRow, CSVValue> call(TableColumn<CSVRow, CSVValue> param) { public TableCell<CSVRow, CSVValue> call(TableColumn<CSVRow, CSVValue> param) {
return new EditableValidationCell(); return new EditableValidationCell(resourceBundle);
} }
} }

View File

@@ -28,7 +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.ValidationState; import ninja.javafx.smartcsv.validation.ValidationError;
import ninja.javafx.smartcsv.validation.Validator; import ninja.javafx.smartcsv.validation.Validator;
/** /**
@@ -41,6 +41,7 @@ public class CSVModel {
private ObservableList<CSVRow> rows = FXCollections.observableArrayList(); private ObservableList<CSVRow> rows = FXCollections.observableArrayList();
private String[] header; private String[] header;
private String filepath; private String filepath;
private ObservableList<ValidationError> validationError = FXCollections.observableArrayList();
public CSVModel(String filepath) { public CSVModel(String filepath) {
this.filepath = filepath; this.filepath = filepath;
@@ -71,6 +72,10 @@ public class CSVModel {
return rows; return rows;
} }
public ObservableList<ValidationError> getValidationError() {
return validationError;
}
/** /**
* adds a new and empty row * adds a new and empty row
* @return the new row * @return the new row
@@ -89,6 +94,7 @@ public class CSVModel {
*/ */
public void setHeader(String[] header) { public void setHeader(String[] header) {
this.header = header; this.header = header;
revalidate();
} }
/** /**
@@ -104,18 +110,37 @@ public class CSVModel {
* walks through the data and validates each value * walks through the data and validates each value
*/ */
private void revalidate() { private void revalidate() {
for (CSVRow row: rows) { validationError.clear();
if (header != null && validator != null) {
addValidationError(validator.isHeaderValid(header));
}
for (int lineNumber = 0; lineNumber < rows.size(); lineNumber++) {
CSVRow row = rows.get(lineNumber);
row.setValidator(validator); row.setValidator(validator);
for (String column: row.getColumns().keySet()) { for (String column: row.getColumns().keySet()) {
CSVValue value = row.getColumns().get(column).getValue(); CSVValue value = row.getColumns().get(column).getValue();
value.setValidator(validator); value.setValidator(validator);
if (validator != null) { if (validator != null) {
value.setValid(validator.isValid(column, value.getValue())); ValidationError validationError = validator.isValid(column, value.getValue(), lineNumber);
if (validationError != null) {
addValidationError(validationError);
value.setValidationError(validationError);
} else { } else {
value.setValid(new ValidationState()); value.setValidationError(null);
}
} else {
value.setValidationError(null);
} }
} }
} }
} }
private void addValidationError(ValidationError validationError) {
if (validationError != null) {
this.validationError.add(validationError);
}
}
} }

View File

@@ -28,7 +28,7 @@ package ninja.javafx.smartcsv.fx.table.model;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import ninja.javafx.smartcsv.validation.ValidationState; import ninja.javafx.smartcsv.validation.ValidationError;
import ninja.javafx.smartcsv.validation.Validator; import ninja.javafx.smartcsv.validation.Validator;
/** /**
@@ -41,7 +41,7 @@ public class CSVValue {
private int rowNumber; private int rowNumber;
private String column; private String column;
private StringProperty value = new SimpleStringProperty(); private StringProperty value = new SimpleStringProperty();
private ValidationState valid = new ValidationState(); private ValidationError valid;
/** /**
* single value of a cell * single value of a cell
@@ -89,7 +89,7 @@ public class CSVValue {
*/ */
public void setValue(String value) { public void setValue(String value) {
if (validator != null) { if (validator != null) {
valid = validator.isValid(column, value); valid = validator.isValid(column, value, rowNumber);
} }
this.value.set(value); this.value.set(value);
} }
@@ -98,7 +98,7 @@ public class CSVValue {
* returns if the value is valid to the rules of the validator * returns if the value is valid to the rules of the validator
* @return * @return
*/ */
public ValidationState getValid() { public ValidationError getValidationError() {
return valid; return valid;
} }
@@ -106,7 +106,7 @@ public class CSVValue {
* sets the state if a value is valid or not * sets the state if a value is valid or not
* @param valid the validation state * @param valid the validation state
*/ */
protected void setValid(ValidationState valid) { protected void setValidationError(ValidationError valid) {
this.valid = valid; this.valid = valid;
} }
} }

View File

@@ -0,0 +1,63 @@
/*
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;
import ninja.javafx.smartcsv.validation.ValidationError;
import ninja.javafx.smartcsv.validation.ValidationMessage;
import java.io.StringWriter;
import java.util.List;
import java.util.ResourceBundle;
import static java.text.MessageFormat.format;
/**
* This class makes validation messages readable in supported languages
*/
public class I18nValidationUtil {
public static String getI18nValidatioMessage(ResourceBundle resourceBundle, ValidationError error) {
List<ValidationMessage> validationMessages = error.getMessages();
StringWriter message = new StringWriter();
for (ValidationMessage validationMessage: validationMessages) {
if (resourceBundle.containsKey(validationMessage.getKey())) {
String resourceText = resourceBundle.getString(validationMessage.getKey());
if (validationMessage.getParameters().length > 0) {
message.append(format(resourceText, validationMessage.getParameters())).append("\n");
} else {
message.append(resourceText).append("\n");
}
} else {
message.append(validationMessage.getKey()).append("\n");
}
}
return message.toString();
}
}

View File

@@ -26,26 +26,45 @@
package ninja.javafx.smartcsv.validation; package ninja.javafx.smartcsv.validation;
import java.io.StringWriter; import java.util.ArrayList;
import java.util.List;
/** /**
* Created by Andreas on 28.11.2015. * This class holds all the error messages
* for a single cell and the information in
* which row the cell is
*/ */
public class ValidationState { public class ValidationError {
private boolean valid = true;
private StringWriter messages = new StringWriter();
public void invalidate(String message) { private List<ValidationMessage> messages = new ArrayList<>();
valid = false; private Integer lineNumber;
messages.append(message).append('\n');
private ValidationError(Integer lineNumber) {
this.lineNumber = lineNumber;
} }
public boolean isValid() { public static ValidationError withLineNumber(int lineNumber) {
return valid; return new ValidationError(lineNumber);
} }
public String error() { public static ValidationError withoutLineNumber() {
return messages.toString(); return new ValidationError(-1);
} }
public Integer getLineNumber() {
return lineNumber;
}
public List<ValidationMessage> getMessages() {
return messages;
}
public ValidationError add(String key, String... parameters) {
messages.add(new ValidationMessage(key, parameters));
return this;
}
public boolean isEmpty() {
return messages.isEmpty();
}
} }

View File

@@ -0,0 +1,79 @@
/*
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.Arrays;
/**
* TODO: DESCRIPTION!!!
*/
public class ValidationMessage {
private String key;
private String[] parameters;
public ValidationMessage(String key, String... parameters) {
this.key = key;
this.parameters = parameters;
}
public String getKey() {
return key;
}
public String[] getParameters() {
return parameters;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ValidationMessage that = (ValidationMessage) o;
if (key != null ? !key.equals(that.key) : that.key != null) return false;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
return Arrays.equals(parameters, that.parameters);
}
@Override
public int hashCode() {
int result = key != null ? key.hashCode() : 0;
result = 31 * result + Arrays.hashCode(parameters);
return result;
}
@Override
public String toString() {
return "ValidationMessage{" +
"key='" + key + '\'' +
", parameters=" + Arrays.toString(parameters) +
'}';
}
}

View File

@@ -33,6 +33,7 @@ import groovy.lang.Script;
import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationFailedException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import static org.apache.commons.validator.GenericValidator.*; import static org.apache.commons.validator.GenericValidator.*;
@@ -73,22 +74,31 @@ public class Validator {
* checks if the value is valid for the column configuration * checks if the value is valid for the column configuration
* @param column the column name * @param column the column name
* @param value the value to check * @param value the value to check
* @return ValidationState with information if valid and if not which error happened * @return ValidationError with information if valid and if not which getMessage happened
*/ */
public ValidationState isValid(String column, String value) { public ValidationError isValid(String column, String value, Integer lineNumber) {
ValidationState result = new ValidationState(); ValidationError result = null;
if (validationConfig != null) { if (validationConfig != null) {
Config columnConfig = getColumnConfig(column); Config columnSectionConfig = getColumnSectionConfig();
if (columnSectionConfig != null) {
Config columnConfig = getColumnConfig(columnSectionConfig, column);
if (columnConfig != null) { if (columnConfig != null) {
checkBlankOrNull(columnConfig, value, result);
ValidationError error = ValidationError.withLineNumber(lineNumber);
checkBlankOrNull(columnConfig, value, error);
if (value != null) { if (value != null) {
checkRegularExpression(columnConfig, value, result); checkRegularExpression(columnConfig, value, error);
checkAlphaNumeric(columnConfig, value, result); checkAlphaNumeric(columnConfig, value, error);
checkDate(columnConfig, value, result); checkDate(columnConfig, value, error);
checkMaxLength(columnConfig, value, result); checkMaxLength(columnConfig, value, error);
checkMinLength(columnConfig, value, result); checkMinLength(columnConfig, value, error);
checkInteger(columnConfig, value, result); checkInteger(columnConfig, value, error);
checkGroovy(column, columnConfig, value, result); checkGroovy(column, columnConfig, value, error);
}
if (!error.isEmpty()) {
result = error;
}
} }
} }
} }
@@ -100,7 +110,7 @@ public class Validator {
// private methods // private methods
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void checkGroovy(String column, Config columnConfig, String value, ValidationState result) { private void checkGroovy(String column, Config columnConfig, String value, ValidationError error) {
String groovyScript = getString(columnConfig, "groovy"); String groovyScript = getString(columnConfig, "groovy");
if (groovyScript != null) { if (groovyScript != null) {
@@ -118,15 +128,15 @@ public class Validator {
try { try {
groovyResult = script.run(); groovyResult = script.run();
} catch (CompilationFailedException e) { } catch (CompilationFailedException e) {
result.invalidate("groovy script '"+groovyScript+"' throws exception: "+e.getMessage()); error.add("validation.message.groovy.exception", groovyScript, e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
if (groovyResult == null) { if (groovyResult == null) {
result.invalidate("groovy script '"+groovyScript+"' returns null"); error.add("validation.message.groovy.return.null", groovyScript);
} }
if (!isScriptResultTrue(groovyResult)) { if (!isScriptResultTrue(groovyResult)) {
result.invalidate(groovyResult.toString()); error.add(groovyResult.toString());
} }
} }
@@ -136,70 +146,75 @@ 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 checkBlankOrNull(Config columnConfig, String value, ValidationState result) { private void checkBlankOrNull(Config columnConfig, String value, ValidationError error) {
if (getBoolean(columnConfig, "not empty")) { if (getBoolean(columnConfig, "not empty")) {
if (isBlankOrNull(value)) { if (isBlankOrNull(value)) {
result.invalidate("should not be empty"); error.add("validation.message.not.empty");
} }
} }
} }
private void checkInteger(Config columnConfig, String value, ValidationState result) { private void checkInteger(Config columnConfig, String value, ValidationError error) {
if (getBoolean(columnConfig, "integer")) { if (getBoolean(columnConfig, "integer")) {
if (!isInt(value)) { if (!isInt(value)) {
result.invalidate("should be an integer"); error.add("validation.message.integer");
} }
} }
} }
private void checkMinLength(Config columnConfig, String value, ValidationState result) { private void checkMinLength(Config columnConfig, String value, ValidationError error) {
Integer minLength = getInteger(columnConfig, "minlength"); Integer minLength = getInteger(columnConfig, "minlength");
if (minLength != null) { if (minLength != null) {
if (!minLength(value, minLength)) { if (!minLength(value, minLength)) {
result.invalidate("has not min length of " + minLength); error.add("validation.message.min.length", minLength.toString());
} }
} }
} }
private void checkMaxLength(Config columnConfig, String value, ValidationState result) { private void checkMaxLength(Config columnConfig, String value, ValidationError error) {
Integer maxLength = getInteger(columnConfig, "maxlength"); Integer maxLength = getInteger(columnConfig, "maxlength");
if (maxLength != null) { if (maxLength != null) {
if (!maxLength(value, maxLength)) { if (!maxLength(value, maxLength)) {
result.invalidate("has not max length of " + maxLength); error.add("validation.message.max.length", maxLength.toString());
} }
} }
} }
private void checkDate(Config columnConfig, String value, ValidationState result) { private void checkDate(Config columnConfig, String value, ValidationError error) {
String dateformat = getString(columnConfig, "date"); String dateformat = getString(columnConfig, "date");
if (dateformat != null && !dateformat.trim().isEmpty()) { if (dateformat != null && !dateformat.trim().isEmpty()) {
if (!isDate(value, dateformat, true)) { if (!isDate(value, dateformat, true)) {
result.invalidate("is not a date of format " + dateformat); error.add("validation.message.date.format", dateformat);
} }
} }
} }
private void checkAlphaNumeric(Config columnConfig, String value, ValidationState result) { private void checkAlphaNumeric(Config columnConfig, String value, ValidationError error) {
if (getBoolean(columnConfig, "alphanumeric")) { if (getBoolean(columnConfig, "alphanumeric")) {
if (!matchRegexp(value, "[0-9a-zA-Z]*")) { if (!matchRegexp(value, "[0-9a-zA-Z]*")) {
result.invalidate("should not be alphanumeric"); error.add("validation.message.alphanumeric");
} }
} }
} }
private void checkRegularExpression(Config columnConfig, String value, ValidationState result) { private void checkRegularExpression(Config columnConfig, String value, ValidationError error) {
String regexp = getString(columnConfig, "regexp"); String regexp = getString(columnConfig, "regexp");
if (regexp != null && !regexp.trim().isEmpty()) { if (regexp != null && !regexp.trim().isEmpty()) {
if (!matchRegexp(value, regexp)) { if (!matchRegexp(value, regexp)) {
result.invalidate("does not match " + regexp); error.add("validation.message.regexp", regexp);
} }
} }
} }
private Config getColumnConfig(String column) { private Config getColumnSectionConfig() {
return validationConfig.hasPath(column) ? validationConfig.getConfig(column) : null; 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) { private String getString(Config columnConfig, String path) {
return columnConfig.hasPath(path) ? columnConfig.getString(path) : null; return columnConfig.hasPath(path) ? columnConfig.getString(path) : null;
} }
@@ -213,4 +228,39 @@ public class Validator {
return columnConfig.hasPath(path) && columnConfig.getBoolean(path); return columnConfig.hasPath(path) && columnConfig.getBoolean(path);
} }
public ValidationError isHeaderValid(String[] headerNames) {
ValidationError result = null;
if (validationConfig != null) {
if (validationConfig.hasPath("headers")) {
Config headerSectionConfig = validationConfig.getConfig("headers");
if (headerSectionConfig.hasPath("list")) {
List<String> headerConfig = headerSectionConfig.getStringList("list");
if (headerConfig != null) {
if (headerNames.length != headerConfig.size()) {
result = ValidationError.withoutLineNumber().add("validation.message.header.length",
Integer.toString(headerNames.length),
Integer.toString(headerConfig.size()));
return result;
}
ValidationError error = ValidationError.withoutLineNumber();
for(int i=0; i<headerConfig.size(); i++) {
String header = headerConfig.get(i);
if (!header.equals(headerNames[i])) {
error.add("validation.message.header.match",
Integer.toString(i),
header,
headerNames[i]);
}
}
if (!error.isEmpty()) {
result = error;
}
}
}
}
}
return result;
}
} }

View File

@@ -12,25 +12,20 @@
<children> <children>
<MenuBar> <MenuBar>
<menus> <menus>
<Menu mnemonicParsing="false" text="File"> <Menu mnemonicParsing="false" text="%menu.file">
<items> <items>
<MenuItem mnemonicParsing="false" onAction="#openCsv" text="Open CSV" /> <MenuItem mnemonicParsing="false" onAction="#openCsv" text="%menu.open.csv" />
<MenuItem mnemonicParsing="false" onAction="#openConfig" text="Open Validation Config" /> <MenuItem mnemonicParsing="false" onAction="#openConfig" text="%menu.open.config" />
<SeparatorMenuItem mnemonicParsing="false" /> <SeparatorMenuItem mnemonicParsing="false" />
<MenuItem mnemonicParsing="false" onAction="#saveCsv" text="Save" /> <MenuItem mnemonicParsing="false" onAction="#saveCsv" text="%menu.save" />
<MenuItem mnemonicParsing="false" onAction="#saveAsCsv" text="Save As" /> <MenuItem mnemonicParsing="false" onAction="#saveAsCsv" text="%menu.save.as" />
<SeparatorMenuItem mnemonicParsing="false" /> <SeparatorMenuItem mnemonicParsing="false" />
<MenuItem mnemonicParsing="false" onAction="#close" text="Close" /> <MenuItem mnemonicParsing="false" onAction="#close" text="%menu.close" />
</items> </items>
</Menu> </Menu>
<Menu mnemonicParsing="false" text="Edit"> <Menu mnemonicParsing="false" text="%menu.help">
<items> <items>
<MenuItem mnemonicParsing="false" text="Delete" /> <MenuItem mnemonicParsing="false" onAction="#about" text="%menu.about" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" onAction="#about" text="About" />
</items> </items>
</Menu> </Menu>
</menus> </menus>
@@ -39,10 +34,26 @@
</VBox> </VBox>
</top> </top>
<center> <center>
<TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="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">
<columns> <columns>
</columns> </columns>
</TableView> </TableView>
</AnchorPane>
<VBox>
<children>
<Label text="%title.validation.errors">
<padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
</padding>
</Label>
<ListView fx:id="errorList" minWidth="10.0" prefWidth="200.0" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</items>
</SplitPane>
</center> </center>
<bottom> <bottom>
<HBox spacing="8.0" BorderPane.alignment="CENTER"> <HBox spacing="8.0" BorderPane.alignment="CENTER">
@@ -54,4 +65,6 @@
</BorderPane.margin> </BorderPane.margin>
</HBox> </HBox>
</bottom> </bottom>
<left>
</left>
</BorderPane> </BorderPane>

View File

@@ -0,0 +1,24 @@
menu.open.csv = Open CSV File
menu.open.config = Open Validation Config
menu.save = Save
menu.save.as = Save As ...
menu.close = Close
menu.about = About
menu.file = File
menu.edit = Edit
menu.help = Help
title.validation.errors = Validation Errors:
# validaton messages
validation.message.not.empty = should not be empty
validation.message.integer = should be an integer
validation.message.alphanumeric = should be alphanumeric
validation.message.groovy.exception = groovy script '{0}' throws exception: {1}
validation.message.groovy.return.null = groovy script '{0}' returns null
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.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}"

View File

@@ -1,52 +1,32 @@
menu.title.import.gpx = Importiere GPX # ae = u00e4
menu.title.file = Datei # Ae = u00c4
menu.title.quit = Beenden # oe = u00f6
menu.title.help = Hilfe # Oe =u00d6
menu.title.settings = Einstellungen # ue = u00fc
menu.title.about = \u00dcber GeoFroggerFX # Ue = u00dc
menu.title.plugins = Plugins # ss = u00df
menu.title.list = Listen
menu.title.list.new = Liste anlegen
menu.title.list.delete = Liste l\u00F6schen
menu.title.sort = Sortieren menu.open.csv = CSV Datei \u00f6ffnen
menu.title.filter = Filtern menu.open.config = Pr\u00fcfkonfiguration \u00f6ffnen
menu.save = Speichern
menu.save.as = Speichern als ...
menu.close = Beenden
menu.about = \u00dcber ...
menu.file = Datei
menu.edit = Bearbeiten
menu.help = Hilfe
title.validation.errors = Fehler in der Datei:
label.text.cache.list=Liste: # validaton messages
label.text.cache.list.count=Anzahl: validation.message.not.empty = Darf nicht leer sein.
label.text.name=Name: validation.message.integer = Muss eine Zahl sein.
label.text.difficulty=Schwierigkeit: validation.message.alphanumeric = Darf nur Zahlen und Buchstaben enthalten.
label.text.terrain=Gel\u00E4nde validation.message.groovy.exception = groovy script '{0}' wirft folgenden Fehler: {1}
label.text.placedBy=Platziert von: validation.message.groovy.return.null = groovy script '{0}' meldet "null"
label.text.owner=Betreut von: validation.message.min.length = Hat nicht die minimale L\u00e4nge von {0}
label.text.date=Datum: validation.message.max.length = Hat nicht die maximale L\u00e4nge von {0}
label.text.type=Typ: validation.message.date.format = Das Datumsformat entspricht nicht {0}
label.text.container=Gr\u00F6\u00dfe: validation.message.regexp = entspricht nicht dem regul\u00e4ren Ausdruck {0}
label.text.shortdescription=Kurze Beschreibung:
label.text.htmldescription=HTML Beschreibung
label.text.longdescription=Lange Beschreibung:
tab.text.descriptions=Beschreibungen validation.message.header.length = Anzahl der \u00dcberschriften ist nicht korrekt! Es sind {0} aber es sollten {1} sein
tab.text.general=Allgemein validation.message.header.match = \u00dcberschrift in Spalte {0} stimmt nicht. "{1}" sollte "{3}" sein
sort.cache.name = GC Code
sort.cache.type = Art
sort.cache.difficulty = Schwierigkeitsgrad
sort.cache.terrain = Gel\u00E4nde
sort.cache.placedBy = Platziert von
sort.cache.owner = Betreut von
dialog.title.about = &#220;ber
dialog.title.new_list = Neue Liste
dialog.label.listname = Name der Liste:
dialog.msg.list.does.exist = Diese Liste existiert schon!
dialog.title.delete_list = Liste l&#246;schen
all.caches = Alle Caches
status.load.all.caches.from.db = Lade alle Caches von der Datenbank.
status.all.cache.lists.loaded = Alle Listen geladen.
status.load.caches.from.db = Lade Caches von der Datenbank.
status.all.caches.loaded = Alle Caches geladen.
status.store.all.caches = Speichere Caches in Datenbank.
status.all.caches.stored = Alle Caches in der Datenbank gespeichert.

View File

@@ -1,53 +0,0 @@
menu.title.import.gpx = Import GPX
menu.title.file = File
menu.title.quit = Quit
menu.title.help = Help
menu.title.settings = Settings
menu.title.about = About GeoFroggerFX
menu.title.plugins = Plugins
menu.title.list = Lists
menu.title.list.new = New list
menu.title.list.delete = Delete list
menu.title.sort = Sort
menu.title.filter = Filter
label.text.cache.list=List:
label.text.cache.list.count=Number:
label.text.name=Name:
label.text.difficulty=Difficulty:
label.text.terrain=Terrain:
label.text.placedBy=Placed By:
label.text.owner=Owner:
label.text.date=Date:
label.text.type=Type:
label.text.container=Container:
label.text.shortdescription=Short description:
label.text.htmldescription=HTML Description
label.text.longdescription=Long description:
tab.text.descriptions=Descriptions
tab.text.general=General
sort.cache.name = GC Code
sort.cache.type = Type
sort.cache.difficulty = Difficulty
sort.cache.terrain = Terrain
sort.cache.placedBy = Placed by
sort.cache.owner = Owner
dialog.title.about = About
dialog.title.new_list = New list
dialog.label.listname = Name of list:
dialog.msg.list.does.exist = List does already exist!
dialog.title.delete_list = Delete list
all.caches = All caches
status.load.all.caches.from.db = Load cache lists from database.
status.all.cache.lists.loaded = All cache lists loaded.
status.load.caches.from.db = Load caches from database.
status.all.caches.loaded = All caches loaded.
status.store.all.caches = Store caches in database.
status.all.caches.stored = All caches are stored in database.

View File

@@ -88,7 +88,7 @@ public class CSVModelTest {
sut.setValidator(validator); sut.setValidator(validator);
// assertion // assertion
verify(validator).isValid(TESTHEADER, TESTVALUE); verify(validator).isValid(TESTHEADER, TESTVALUE, 0);
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -4,6 +4,8 @@ import ninja.javafx.smartcsv.validation.Validator;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; 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.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -53,6 +55,6 @@ public class CSVValueTest {
sut.setValue(VALUE); sut.setValue(VALUE);
// assertion // assertion
verify(validator).isValid(COLUMN, VALUE); verify(validator).isValid(eq(COLUMN), eq(VALUE), anyInt());
} }
} }

View File

@@ -0,0 +1,58 @@
package ninja.javafx.smartcsv.validation;
import com.typesafe.config.Config;
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);
}
return columnSectionConfig;
}
}

View File

@@ -0,0 +1,116 @@
/*
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.typesafe.config.Config;
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 org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* unit test for header validator
*/
@RunWith(Parameterized.class)
public class HeaderValidationTest {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private Config config;
private Boolean expectedResult;
private List<ValidationMessage> expectedErrors;
private String[] headerNames;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// subject under test
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private Validator sut;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameterized constructor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public HeaderValidationTest(String[] configHeaderNames,
String[] headerNames,
Boolean expectedResult,
List<ValidationMessage> expectedErrors) {
this.config = headerSectionConfig(configHeaderNames);
this.headerNames = headerNames;
this.expectedResult = expectedResult;
this.expectedErrors = expectedErrors;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// init
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Before
public void initialize() {
sut = new Validator(config);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// tests
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Test
public void validation() {
// execution
ValidationError result = sut.isHeaderValid(headerNames);
// assertion
assertThat(result == null, is(expectedResult));
if (!expectedResult) {
assertTrue(result.getMessages().containsAll(expectedErrors));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameters for tests
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@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")) }
});
}
}

View File

@@ -9,10 +9,10 @@ import org.junit.runners.Parameterized;
import java.util.Collection; import java.util.Collection;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static ninja.javafx.smartcsv.validation.ConfigMock.columnSectionConfig;
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;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/** /**
* unit test for validator * unit test for validator
@@ -27,7 +27,7 @@ public class ValidatorTest {
private String column; private String column;
private String value; private String value;
private Boolean expectedResult; private Boolean expectedResult;
private String expectedError; private ValidationMessage expectedError;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -45,8 +45,8 @@ public class ValidatorTest {
String column, String column,
String value, String value,
Boolean expectedResult, Boolean expectedResult,
String expectedError) { ValidationMessage expectedError) {
this.config = config(configcolumn, configValidation, configValue); this.config = columnSectionConfig(configcolumn, configValidation, configValue);
this.column = column; this.column = column;
this.value = value; this.value = value;
this.expectedResult = expectedResult; this.expectedResult = expectedResult;
@@ -69,38 +69,15 @@ public class ValidatorTest {
@Test @Test
public void validation() { public void validation() {
// execution // execution
ValidationState result = sut.isValid(column, value); ValidationError result = sut.isValid(column, value, 0);
// assertion // assertion
assertThat(result.isValid(), is(expectedResult)); assertThat(result == null, is(expectedResult));
if (!expectedResult) { if (!expectedResult) {
assertThat(result.error(), is(expectedError)); assertThat(result.getMessages(), contains(expectedError));
} }
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// mocks
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private Config config(String column, String validation, Object value) {
Config columnConfig = mock(Config.class);
Config validatorConfig = mock(Config.class);
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);
}
return columnConfig;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameters for tests // parameters for tests
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -108,22 +85,22 @@ public class ValidatorTest {
public static Collection validationConfigurations() { public static Collection validationConfigurations() {
return asList(new Object[][] { return asList(new Object[][] {
{ "column", "not empty", true, "column", "value", true, null }, { "column", "not empty", true, "column", "value", true, null },
{ "column", "not empty", true, "column", "", false, "should not be empty\n" }, { "column", "not empty", true, "column", "", false, new ValidationMessage("validation.message.not.empty") },
{ "column", "not empty", true, "column", null, false, "should not be empty\n" }, { "column", "not empty", true, "column", null, false, new ValidationMessage("validation.message.not.empty") },
{ "column", "integer", true, "column", "999", true, null }, { "column", "integer", true, "column", "999", true, null },
{ "column", "integer", true, "column", "a", false, "should be an integer\n" }, { "column", "integer", true, "column", "a", false, new ValidationMessage("validation.message.integer") },
{ "column", "minlength", 2, "column", "12", true, null }, { "column", "minlength", 2, "column", "12", true, null },
{ "column", "minlength", 2, "column", "1", false, "has not min length of 2\n" }, { "column", "minlength", 2, "column", "1", false, new ValidationMessage("validation.message.min.length", "2") },
{ "column", "maxlength", 2, "column", "12", true, null }, { "column", "maxlength", 2, "column", "12", true, null },
{ "column", "maxlength", 2, "column", "123", false, "has not max length of 2\n" }, { "column", "maxlength", 2, "column", "123", false, new ValidationMessage("validation.message.max.length", "2") },
{ "column", "date", "yyyyMMdd", "column", "20151127", true, null }, { "column", "date", "yyyyMMdd", "column", "20151127", true, null },
{ "column", "date", "yyyyMMdd", "column", "27.11.2015", false, "is not a date of format yyyyMMdd\n" }, { "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", true, null },
{ "column", "alphanumeric", true, "column", "-abcABC123", false, "should not be alphanumeric\n" }, { "column", "alphanumeric", true, "column", "-abcABC123", false, new ValidationMessage("validation.message.alphanumeric") },
{ "column", "regexp", "[a-z]*", "column", "abc", true, null }, { "column", "regexp", "[a-z]*", "column", "abc", true, null },
{ "column", "regexp", "[a-z]*", "column", "abcA", false, "does not match [a-z]*\n" }, { "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", "abcdef", true, null },
{ "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, "no a inside\n" }, { "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, new ValidationMessage("no a inside") },
}); });
} }