From 9278bfa13a0c7ea1da449e6f6386a59bad815185 Mon Sep 17 00:00:00 2001 From: Andreas Billmann Date: Thu, 17 Dec 2015 20:44:07 +0100 Subject: [PATCH] add support for i18n validation messages --- .../smartcsv/fx/SmartCSVController.java | 9 ++- .../fx/list/ValidationErrorListCell.java | 12 ++- .../fx/table/EditableValidationCell.java | 10 ++- .../fx/table/ValidationCellFactory.java | 10 ++- .../smartcsv/fx/util/I18nValidationUtil.java | 52 ++++++++++++ .../smartcsv/validation/ValidationError.java | 16 ++-- .../javafx/smartcsv/validation/Validator.java | 79 ++++++++++--------- .../validation/HeaderValidationTest.java | 17 ++-- .../smartcsv/validation/ValidatorTest.java | 21 ++--- 9 files changed, 155 insertions(+), 71 deletions(-) create mode 100644 src/main/java/ninja/javafx/smartcsv/fx/util/I18nValidationUtil.java diff --git a/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java b/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java index 17690ae..53129e4 100644 --- a/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java +++ b/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java @@ -90,10 +90,10 @@ public class SmartCSVController extends FXMLController { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // injections + // members //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - private ValidationCellFactory cellFactory = new ValidationCellFactory(); + private ValidationCellFactory cellFactory; private final LoadCSVService loadCSVService = new LoadCSVService(); private final SaveCSVService saveCSVService = new SaveCSVService(); private CSVModel model; @@ -105,9 +105,10 @@ public class SmartCSVController extends FXMLController { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override - public void initialize(URL location, ResourceBundle resources) { + public void initialize(URL location, ResourceBundle resourceBundle) { + cellFactory = new ValidationCellFactory(resourceBundle); stateline.setVisible(false); - errorList.setCellFactory(param -> new ValidationErrorListCell()); + errorList.setCellFactory(param -> new ValidationErrorListCell(resourceBundle)); errorList.getSelectionModel().selectedItemProperty().addListener( observable -> scrollToError() ); diff --git a/src/main/java/ninja/javafx/smartcsv/fx/list/ValidationErrorListCell.java b/src/main/java/ninja/javafx/smartcsv/fx/list/ValidationErrorListCell.java index 37a4132..14b5166 100644 --- a/src/main/java/ninja/javafx/smartcsv/fx/list/ValidationErrorListCell.java +++ b/src/main/java/ninja/javafx/smartcsv/fx/list/ValidationErrorListCell.java @@ -30,12 +30,20 @@ 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 { - private Text text; + private ResourceBundle resourceBundle; + + public ValidationErrorListCell(ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } @Override public void updateItem(ValidationError validationError, boolean empty) { @@ -55,7 +63,7 @@ public class ValidationErrorListCell extends ListCell { private void addContent(ValidationError validationError) { setText(null); - text = new Text(validationError.getMessage()); + Text text = new Text(getI18nValidatioMessage(resourceBundle, validationError.getMessages())); text.setWrappingWidth(180); setGraphic(text); } diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java b/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java index 6842527..93c0a5d 100644 --- a/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java @@ -34,7 +34,10 @@ import javafx.scene.input.KeyCode; import ninja.javafx.smartcsv.fx.table.model.CSVRow; import ninja.javafx.smartcsv.fx.table.model.CSVValue; +import java.util.ResourceBundle; + import static javafx.application.Platform.runLater; +import static ninja.javafx.smartcsv.fx.util.I18nValidationUtil.getI18nValidatioMessage; /** * Created by Andreas on 27.11.2015. @@ -42,6 +45,11 @@ import static javafx.application.Platform.runLater; public class EditableValidationCell extends TableCell { private ValueTextField textField; + private ResourceBundle resourceBundle; + + public EditableValidationCell(ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } @Override public void startEdit() { @@ -69,7 +77,7 @@ public class EditableValidationCell extends TableCell { setTooltip(null); } else if (item.getValidationError() != null) { setStyle("-fx-background-color: #ff8888"); - setTooltip(new Tooltip(item.getValidationError().getMessage())); + setTooltip(new Tooltip(getI18nValidatioMessage(resourceBundle, item.getValidationError().getMessages()))); } if (item == null || empty) { diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java b/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java index 5809ff9..457ca60 100644 --- a/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java @@ -32,13 +32,21 @@ import javafx.util.Callback; import ninja.javafx.smartcsv.fx.table.model.CSVRow; import ninja.javafx.smartcsv.fx.table.model.CSVValue; +import java.util.ResourceBundle; + /** * Created by Andreas on 18.11.2015. */ public class ValidationCellFactory implements Callback, TableCell> { + private ResourceBundle resourceBundle; + + public ValidationCellFactory(ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + @Override public TableCell call(TableColumn param) { - return new EditableValidationCell(); + return new EditableValidationCell(resourceBundle); } } diff --git a/src/main/java/ninja/javafx/smartcsv/fx/util/I18nValidationUtil.java b/src/main/java/ninja/javafx/smartcsv/fx/util/I18nValidationUtil.java new file mode 100644 index 0000000..dae9823 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/util/I18nValidationUtil.java @@ -0,0 +1,52 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 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 java.io.StringWriter; +import java.util.List; +import java.util.ResourceBundle; + +/** + * This class makes validation messages readable in supported languages + */ +public class I18nValidationUtil { + + public static String getI18nValidatioMessage(ResourceBundle resourceBundle, List messages) { + + StringWriter message = new StringWriter(); + for (String key: messages) { + if (resourceBundle.containsKey(key)) { + message.append(resourceBundle.getString(key)).append("\n"); + } else { + message.append(key).append("\n"); + } + } + + return message.toString(); + } + +} diff --git a/src/main/java/ninja/javafx/smartcsv/validation/ValidationError.java b/src/main/java/ninja/javafx/smartcsv/validation/ValidationError.java index 9c6f193..5380a5e 100644 --- a/src/main/java/ninja/javafx/smartcsv/validation/ValidationError.java +++ b/src/main/java/ninja/javafx/smartcsv/validation/ValidationError.java @@ -26,20 +26,22 @@ package ninja.javafx.smartcsv.validation; +import java.util.List; + /** * Created by Andreas on 28.11.2015. */ public class ValidationError { - private String message; + private List messages; private Integer lineNumber; - public ValidationError(String message) { - this(message, -1); + public ValidationError(List messages) { + this(messages, -1); } - public ValidationError(String message, Integer lineNumber) { - this.message = message; + public ValidationError(List messages, Integer lineNumber) { + this.messages = messages; this.lineNumber = lineNumber; } @@ -47,8 +49,8 @@ public class ValidationError { return lineNumber; } - public String getMessage() { - return message; + public List getMessages() { + return messages; } } diff --git a/src/main/java/ninja/javafx/smartcsv/validation/Validator.java b/src/main/java/ninja/javafx/smartcsv/validation/Validator.java index 80010b7..f462249 100644 --- a/src/main/java/ninja/javafx/smartcsv/validation/Validator.java +++ b/src/main/java/ninja/javafx/smartcsv/validation/Validator.java @@ -32,11 +32,12 @@ import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilationFailedException; -import java.io.StringWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import static java.util.Collections.singletonList; import static org.apache.commons.validator.GenericValidator.*; /** @@ -85,20 +86,20 @@ public class Validator { Config columnConfig = getColumnConfig(columnSectionConfig, column); if (columnConfig != null) { - StringWriter errorMessage = new StringWriter(); - checkBlankOrNull(columnConfig, value, errorMessage); + List errorMessages = new ArrayList<>(); + checkBlankOrNull(columnConfig, value, errorMessages); if (value != null) { - checkRegularExpression(columnConfig, value, errorMessage); - checkAlphaNumeric(columnConfig, value, errorMessage); - checkDate(columnConfig, value, errorMessage); - checkMaxLength(columnConfig, value, errorMessage); - checkMinLength(columnConfig, value, errorMessage); - checkInteger(columnConfig, value, errorMessage); - checkGroovy(column, columnConfig, value, errorMessage); + checkRegularExpression(columnConfig, value, errorMessages); + checkAlphaNumeric(columnConfig, value, errorMessages); + checkDate(columnConfig, value, errorMessages); + checkMaxLength(columnConfig, value, errorMessages); + checkMinLength(columnConfig, value, errorMessages); + checkInteger(columnConfig, value, errorMessages); + checkGroovy(column, columnConfig, value, errorMessages); } - if (!errorMessage.toString().isEmpty()) { - result = new ValidationError(errorMessage.toString(), lineNumber); + if (!errorMessages.isEmpty()) { + result = new ValidationError(errorMessages, lineNumber); } } } @@ -111,7 +112,7 @@ public class Validator { // private methods //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - private void checkGroovy(String column, Config columnConfig, String value, StringWriter result) { + private void checkGroovy(String column, Config columnConfig, String value, List result) { String groovyScript = getString(columnConfig, "groovy"); if (groovyScript != null) { @@ -129,15 +130,15 @@ public class Validator { try { groovyResult = script.run(); } catch (CompilationFailedException e) { - result.append("groovy script '"+groovyScript+"' throws exception: "+e.getMessage()).append('\n'); + result.add("groovy script '"+groovyScript+"' throws exception: "+e.getMessage()); e.printStackTrace(); } if (groovyResult == null) { - result.append("groovy script '"+groovyScript+"' returns null").append('\n'); + result.add("groovy script '"+groovyScript+"' returns null"); } if (!isScriptResultTrue(groovyResult)) { - result.append(groovyResult.toString()).append('\n'); + result.add(groovyResult.toString()); } } @@ -147,62 +148,62 @@ public class Validator { return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true"); } - private void checkBlankOrNull(Config columnConfig, String value, StringWriter result) { + private void checkBlankOrNull(Config columnConfig, String value, List result) { if (getBoolean(columnConfig, "not empty")) { if (isBlankOrNull(value)) { - result.append("should not be empty").append('\n'); + result.add("should not be empty"); } } } - private void checkInteger(Config columnConfig, String value, StringWriter result) { + private void checkInteger(Config columnConfig, String value, List result) { if (getBoolean(columnConfig, "integer")) { if (!isInt(value)) { - result.append("should be an integer").append('\n'); + result.add("should be an integer"); } } } - private void checkMinLength(Config columnConfig, String value, StringWriter result) { + private void checkMinLength(Config columnConfig, String value, List result) { Integer minLength = getInteger(columnConfig, "minlength"); if (minLength != null) { if (!minLength(value, minLength)) { - result.append("has not min length of " + minLength).append('\n'); + result.add("has not min length of " + minLength); } } } - private void checkMaxLength(Config columnConfig, String value, StringWriter result) { + private void checkMaxLength(Config columnConfig, String value, List result) { Integer maxLength = getInteger(columnConfig, "maxlength"); if (maxLength != null) { if (!maxLength(value, maxLength)) { - result.append("has not max length of " + maxLength).append('\n'); + result.add("has not max length of " + maxLength); } } } - private void checkDate(Config columnConfig, String value, StringWriter result) { + private void checkDate(Config columnConfig, String value, List result) { String dateformat = getString(columnConfig, "date"); if (dateformat != null && !dateformat.trim().isEmpty()) { if (!isDate(value, dateformat, true)) { - result.append("is not a date of format " + dateformat).append('\n'); + result.add("is not a date of format " + dateformat); } } } - private void checkAlphaNumeric(Config columnConfig, String value, StringWriter result) { + private void checkAlphaNumeric(Config columnConfig, String value, List result) { if (getBoolean(columnConfig, "alphanumeric")) { if (!matchRegexp(value, "[0-9a-zA-Z]*")) { - result.append("should not be alphanumeric").append('\n'); + result.add("should not be alphanumeric"); } } } - private void checkRegularExpression(Config columnConfig, String value, StringWriter result) { + private void checkRegularExpression(Config columnConfig, String value, List result) { String regexp = getString(columnConfig, "regexp"); if (regexp != null && !regexp.trim().isEmpty()) { if (!matchRegexp(value, regexp)) { - result.append("does not match " + regexp).append('\n'); + result.add("does not match " + regexp); } } } @@ -238,29 +239,29 @@ public class Validator { List headerConfig = headerSectionConfig.getStringList("list"); if (headerConfig != null) { if (headerNames.length != headerConfig.size()) { - result = new ValidationError("number of headers is not correct! there are " + - headerNames.length + - " but there should be " + - headerConfig.size()+ "\n"); + result = new ValidationError(singletonList("number of headers is not correct! there are " + + headerNames.length + + " but there should be " + + headerConfig.size())); return result; } - StringWriter errorMessage = new StringWriter(); + List errorMessages = new ArrayList<>(); for(int i=0; i expectedErrors; private String[] headerNames; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -64,11 +67,11 @@ public class HeaderValidationTest { public HeaderValidationTest(String[] configHeaderNames, String[] headerNames, Boolean expectedResult, - String expectedError) { + List expectedErrors) { this.config = headerSectionConfig(configHeaderNames); this.headerNames = headerNames; this.expectedResult = expectedResult; - this.expectedError = expectedError; + this.expectedErrors = expectedErrors; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -91,7 +94,7 @@ public class HeaderValidationTest { // assertion assertThat(result == null, is(expectedResult)); if (!expectedResult) { - assertThat(result.getMessage(), is(expectedError)); + assertTrue(result.getMessages().containsAll(expectedErrors)); } } @@ -103,9 +106,9 @@ public class HeaderValidationTest { 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, "header number 0 does not match \"a\" should be \"b\"\n" }, - { new String[] {"a"}, new String[] {"a","b"}, false, "number of headers is not correct! there are 2 but there should be 1\n" }, - { new String[] {"a", "b"}, new String[] {"b", "a"}, false, "header number 0 does not match \"a\" should be \"b\"\nheader number 1 does not match \"b\" should be \"a\"\n" } + { new String[] {"a"}, new String[] {"b"}, false, Arrays.asList("header number 0 does not match \"a\" should be \"b\"") }, + { new String[] {"a"}, new String[] {"a","b"}, false, Arrays.asList("number of headers is not correct! there are 2 but there should be 1") }, + { new String[] {"a", "b"}, new String[] {"b", "a"}, false, Arrays.asList("header number 0 does not match \"a\" should be \"b\"", "header number 1 does not match \"b\" should be \"a\"") } }); } } diff --git a/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java b/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java index 48faa7b..b8c537d 100644 --- a/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java +++ b/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java @@ -10,6 +10,7 @@ import java.util.Collection; 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.junit.Assert.assertThat; @@ -73,7 +74,7 @@ public class ValidatorTest { // assertion assertThat(result == null, is(expectedResult)); if (!expectedResult) { - assertThat(result.getMessage(), is(expectedError)); + assertThat(result.getMessages(), contains(expectedError)); } } @@ -84,22 +85,22 @@ public class ValidatorTest { public static Collection validationConfigurations() { return asList(new Object[][] { { "column", "not empty", true, "column", "value", true, null }, - { "column", "not empty", true, "column", "", false, "should not be empty\n" }, - { "column", "not empty", true, "column", null, false, "should not be empty\n" }, + { "column", "not empty", true, "column", "", false, "should not be empty" }, + { "column", "not empty", true, "column", null, false, "should not be empty" }, { "column", "integer", true, "column", "999", true, null }, - { "column", "integer", true, "column", "a", false, "should be an integer\n" }, + { "column", "integer", true, "column", "a", false, "should be an integer" }, { "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, "has not min length of 2" }, { "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, "has not max length of 2" }, { "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, "is not a date of format yyyyMMdd" }, { "column", "alphanumeric", true, "column", "abcABC123", true, null }, - { "column", "alphanumeric", true, "column", "-abcABC123", false, "should not be alphanumeric\n" }, + { "column", "alphanumeric", true, "column", "-abcABC123", false, "should not be alphanumeric" }, { "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, "does not match [a-z]*" }, { "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "abcdef", true, null }, - { "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, "no a inside\n" }, + { "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, "no a inside" }, }); }