diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidator.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..35b0d85f594480a20dd3d6010e5c9088b64ddfa2 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidator.java @@ -0,0 +1,54 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver.validation; + +import org.apache.commons.lang.StringUtils; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; + +/** + * + * + * @author Franz-Josef Elmer + */ +abstract class AbstractValidator implements IValidator +{ + private final boolean allowEmptyValues; + + AbstractValidator(boolean allowEmptyValues) + { + this.allowEmptyValues = allowEmptyValues; + } + + public final void assertValid(String value) + { + if (allowEmptyValues) + { + if (StringUtils.isBlank(value)) + { + return; + } + } else if (StringUtils.isBlank(value)) + { + throw new UserFailureException("Empty value is not allowed."); + } + assertValidNonEmptyValue(value); + } + + protected abstract void assertValidNonEmptyValue(String value); + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidatorFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3195ff3c9f75dc41509c271cb2533e48be1e2d1e --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidatorFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver.validation; + +import java.util.Properties; + +import ch.systemsx.cisd.common.utilities.PropertyUtils; + +/** + * + * + * @author Franz-Josef Elmer + */ +abstract class AbstractValidatorFactory implements IValidatorFactory +{ + static final String ALLOW_EMPTY_VALUES_KEY = "allow-empty-values"; + + protected final boolean allowEmptyValues; + + AbstractValidatorFactory(Properties properties) + { + allowEmptyValues = PropertyUtils.getBoolean(properties, ALLOW_EMPTY_VALUES_KEY, false); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java index a38ddad28ba4bbe5569a766bc85019422e318319..ba470d65bac8c5e4e71e2b5480a16cd88a92edb4 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java @@ -58,8 +58,8 @@ class ColumnDefinition IColumnHeaderValidator headerValidator; if (headerValidatorName == null) { - headerValidator = - new RegExBasedValidator(properties.getProperty(HEADER_PATTERN_KEY, ".*")); + String headerPattern = properties.getProperty(HEADER_PATTERN_KEY, ".*"); + headerValidator = new RegExBasedValidator(false, headerPattern); } else { headerValidator = diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java index 281505896dee9529c785faddfaf3251d43d596af..0a95611fed9c6be15763b561caed545e4e7ff59b 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java @@ -22,11 +22,11 @@ import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; import ch.systemsx.cisd.common.exceptions.UserFailureException; /** - * + * Factory for validators of numeric values. * * @author Franz-Josef Elmer */ -class NumericValidatorFactory implements IValidatorFactory +class NumericValidatorFactory extends AbstractValidatorFactory { static final String VALUE_RANGE_KEY = "value-range"; @@ -91,6 +91,11 @@ class NumericValidatorFactory implements IValidatorFactory throw new ConfigurationFailureException("Invalid maximum in range definition: " + rangeDescription); } + if (maximum < minimum) + { + throw new ConfigurationFailureException( + "Minimum is larger than maximum in range description: " + rangeDescription); + } } void assertInRange(double number) @@ -108,16 +113,18 @@ class NumericValidatorFactory implements IValidatorFactory } } - private final static class NumericValidator implements IValidator + private final static class NumericValidator extends AbstractValidator { private final Range rangeOrNull; - NumericValidator(Range rangeOrNull) + NumericValidator(boolean allowEmptyValues, Range rangeOrNull) { + super(allowEmptyValues); this.rangeOrNull = rangeOrNull; } - public void assertValid(String value) + @Override + protected void assertValidNonEmptyValue(String value) { double number = Double.parseDouble(value); if (rangeOrNull != null) @@ -132,8 +139,10 @@ class NumericValidatorFactory implements IValidatorFactory NumericValidatorFactory(Properties properties) { + super(properties); String valueRange = properties.getProperty(VALUE_RANGE_KEY); - validator = new NumericValidator(valueRange == null ? null : new Range(valueRange)); + Range rangeOrNull = valueRange == null ? null : new Range(valueRange); + validator = new NumericValidator(allowEmptyValues, rangeOrNull); } public IValidator createValidator() diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java index 0b05ceeaeb0376f65d7329f95555150078fe1030..d17c224d737321576fb644586d954bb16b12d47a 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java @@ -21,25 +21,27 @@ import java.util.regex.Pattern; import ch.systemsx.cisd.common.exceptions.UserFailureException; /** - * + * Validator based on a regular expression. * * @author Franz-Josef Elmer */ -class RegExBasedValidator implements IValidator, IColumnHeaderValidator +class RegExBasedValidator extends AbstractValidator implements IColumnHeaderValidator { private final Pattern pattern; - RegExBasedValidator(String regularExpression) + RegExBasedValidator(boolean allowEmptyValues, String regularExpression) { + super(allowEmptyValues); pattern = Pattern.compile(regularExpression); } - public void assertValid(String value) + @Override + protected void assertValidNonEmptyValue(String value) { if (isValidHeader(value) == false) { throw new UserFailureException("'" + value - + "' dosn't match the following regular expression: " + pattern); + + "' doesn't match the following regular expression: " + pattern); } } diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java index 5c64afb0bb1b0a00f147a11cff578b2cddd4e8d9..3241e22e92c5cc3fde3ca9a89435805da957e6f1 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java @@ -25,7 +25,7 @@ import ch.systemsx.cisd.common.utilities.PropertyUtils; * * @author Franz-Josef Elmer */ -class StringValidatorFactory implements IValidatorFactory +class StringValidatorFactory extends AbstractValidatorFactory { static final String VALUE_PATTERN_KEY = "value-pattern"; @@ -33,8 +33,9 @@ class StringValidatorFactory implements IValidatorFactory StringValidatorFactory(Properties properties) { + super(properties); String regex = PropertyUtils.getMandatoryProperty(properties, VALUE_PATTERN_KEY); - validator = new RegExBasedValidator(regex); + validator = new RegExBasedValidator(allowEmptyValues, regex); } public IValidator createValidator() diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java index 8022f81269366fcd2815a730b7cef5a29d6ba993..2685e2c86b644e2d23f6a5f26538ec64ee8f741f 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java +++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java @@ -39,7 +39,7 @@ class UniqueValidatorFactory implements IValidatorFactory validator = AnyValidatorFactory.INSTANCE.createValidator(); } else { - validator = new RegExBasedValidator(regex); + validator = new RegExBasedValidator(false, regex); } } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java index 19c7f130fe5707b8f1e9d085ba202d08f6dd2e06..42f9c19d4b637b8247626a54482ef199a263bfa5 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java @@ -47,7 +47,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit } @Test - public void testMissingOpeningBracket() + public void testMissingOpeningBracketInRangeDescription() { try { @@ -60,7 +60,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit } @Test - public void testMissingClosingBracket() + public void testMissingClosingBracketInRangeDescription() { try { @@ -73,7 +73,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit } @Test - public void testMissingComma() + public void testMissingCommaInRangeDescription() { try { @@ -85,6 +85,45 @@ public class NumericValidatorFactoryTest extends AssertJUnit } } + @Test + public void testInvalidMinimumInRangeDescription() + { + try + { + createValidator("(abc,3)"); + fail("ConfigurationFailureException expected"); + } catch (ConfigurationFailureException ex) + { + assertEquals("Invalid minimum in range definition: (abc,3)", ex.getMessage()); + } + } + + @Test + public void testInvalidMaximumInRangeDescription() + { + try + { + createValidator("(1,abc)"); + fail("ConfigurationFailureException expected"); + } catch (ConfigurationFailureException ex) + { + assertEquals("Invalid maximum in range definition: (1,abc)", ex.getMessage()); + } + } + + @Test + public void testMinLargerThanMaxInRangeDescription() + { + try + { + createValidator("(1,0.999)"); + fail("ConfigurationFailureException expected"); + } catch (ConfigurationFailureException ex) + { + assertEquals("Minimum is larger than maximum in range description: (1,0.999)", ex.getMessage()); + } + } + @Test public void testInvalidNumber() { @@ -92,8 +131,35 @@ public class NumericValidatorFactoryTest extends AssertJUnit IValidator validator = validatorFactory.createValidator(); assertNotANumber(validator, "abc"); - assertNotANumber(validator, "-0-"); - assertNotANumber(validator, ""); + assertNotANumber(validator, " -0-"); + } + + @Test + public void testEmptyValue() + { + NumericValidatorFactory validatorFactory = new NumericValidatorFactory(new Properties()); + IValidator validator = validatorFactory.createValidator(); + + assertFailingOnBlankValue(validator, null); + assertFailingOnBlankValue(validator, ""); + assertFailingOnBlankValue(validator, " "); + } + + @Test + public void testAllowEmptyValues() + { + Properties properties = new Properties(); + properties.setProperty(NumericValidatorFactory.ALLOW_EMPTY_VALUES_KEY, "true"); + properties.setProperty(NumericValidatorFactory.VALUE_RANGE_KEY, "(0,1]"); + NumericValidatorFactory validatorFactory = new NumericValidatorFactory(properties); + IValidator validator = validatorFactory.createValidator(); + + validator.assertValid(null); + validator.assertValid(""); + validator.assertValid(" "); + validator.assertValid(" 0.9999"); + assertFailingToLarge("> 1.0", validator, "1.25"); + assertFailingToSmall("<= 0.0", validator, "0.0"); } @Test @@ -144,6 +210,18 @@ public class NumericValidatorFactoryTest extends AssertJUnit return validatorFactory.createValidator(); } + private void assertFailingOnBlankValue(IValidator validator, String value) + { + try + { + validator.assertValid(value); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("Empty value is not allowed.", ex.getMessage()); + } + } + private void assertNotANumber(IValidator validator, String string) { try @@ -152,7 +230,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit fail("NumberFormatException expected"); } catch (NumberFormatException ex) { - AssertionUtil.assertContains(string, ex.getMessage()); + AssertionUtil.assertContains(string.trim(), ex.getMessage()); } } diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5f1c06b796bed84a4f7a2bb057b33f48d4ebfe97 --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactoryTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver.validation; + +import java.util.Properties; + +import org.testng.AssertJUnit; +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.exceptions.UserFailureException; + +/** + * + * + * @author Franz-Josef Elmer + */ +public class StringValidatorFactoryTest extends AssertJUnit +{ + @Test + public void testMissingPattern() + { + try + { + new StringValidatorFactory(new Properties()); + fail("ConfigurationFailureException expected"); + } catch (ConfigurationFailureException ex) + { + assertEquals("Given key '" + StringValidatorFactory.VALUE_PATTERN_KEY + + "' not found in properties '[]'", ex.getMessage()); + } + } + + @Test + public void testEmptyValuesNotAllowed() + { + Properties properties = new Properties(); + properties.setProperty(StringValidatorFactory.VALUE_PATTERN_KEY, "a.*"); + StringValidatorFactory factory = new StringValidatorFactory(properties); + IValidator validator = factory.createValidator(); + + validator.assertValid("a"); + validator.assertValid("a1"); + validator.assertValid("abc"); + try + { + validator.assertValid("bc"); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("'bc' doesn't match the following regular expression: a.*", ex.getMessage()); + } + try + { + validator.assertValid(" "); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("Empty value is not allowed.", ex.getMessage()); + } + } + + @Test + public void testEmptyValuesAllowed() + { + Properties properties = new Properties(); + properties.setProperty(StringValidatorFactory.VALUE_PATTERN_KEY, "a.*"); + properties.setProperty(StringValidatorFactory.ALLOW_EMPTY_VALUES_KEY, "yes"); + StringValidatorFactory factory = new StringValidatorFactory(properties); + IValidator validator = factory.createValidator(); + + validator.assertValid("a"); + validator.assertValid("a1"); + validator.assertValid("abc"); + validator.assertValid(null); + validator.assertValid(""); + validator.assertValid(" "); + try + { + validator.assertValid("bc"); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("'bc' doesn't match the following regular expression: a.*", ex.getMessage()); + } + } +} diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d62579727c95774085fa5ab1fe564e28692789c6 --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactoryTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.etlserver.validation; + +import java.util.Properties; + +import org.testng.AssertJUnit; +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; + +/** + * + * + * @author Franz-Josef Elmer + */ +public class UniqueValidatorFactoryTest extends AssertJUnit +{ + @Test + public void testNoPattern() + { + UniqueValidatorFactory factory = new UniqueValidatorFactory(new Properties()); + IValidator validator = factory.createValidator(); + + validator.assertValid("a"); + validator.assertValid("b"); + try + { + validator.assertValid("a"); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("The following value is not unique: a", ex.getMessage()); + } + } + + @Test + public void testWithPattern() + { + Properties properties = new Properties(); + properties.setProperty(StringValidatorFactory.VALUE_PATTERN_KEY, "a[0-9]*"); + UniqueValidatorFactory factory = new UniqueValidatorFactory(properties); + IValidator validator = factory.createValidator(); + + validator.assertValid("a"); + validator.assertValid("a1"); + validator.assertValid("a123"); + try + { + validator.assertValid("ab"); + fail("UserFailureException expected"); + } catch (UserFailureException ex) + { + assertEquals("'ab' doesn't match the following regular expression: a[0-9]*", ex.getMessage()); + } + } + + @Test + public void testCreateValidatorReturnsFreshValidator() + { + UniqueValidatorFactory factory = new UniqueValidatorFactory(new Properties()); + IValidator validator = factory.createValidator(); + + validator.assertValid("a"); + + validator = factory.createValidator(); + + validator.assertValid("a"); + } +}