Fixed FELIX-3758, FELIX-3757 & FELIX-3756:
- validation of required attributes without min, max or optionValues was not performed;
- validation of attributes with cardinality != 0 was not performed correctly;
- added a validation for values of invalid types to ensure they are not accepted as-is.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1409877 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/metatype/src/main/java/org/apache/felix/metatype/AD.java b/metatype/src/main/java/org/apache/felix/metatype/AD.java
index 8899ccf..a083f68 100644
--- a/metatype/src/main/java/org/apache/felix/metatype/AD.java
+++ b/metatype/src/main/java/org/apache/felix/metatype/AD.java
@@ -47,6 +47,12 @@
/**
* The message returned from the {@link #validate(String)} method if the
+ * value is invalid considering its type (value is "%invalid value").
+ */
+ public static final String VALIDATE_INVALID_VALUE = "%invalid value";
+
+ /**
+ * The message returned from the {@link #validate(String)} method if the
* value is greater than the specified {@link #getMax() maximum value}
* (value is "%greater than maximum").
*/
@@ -175,109 +181,7 @@
*/
public String validate( String valueString )
{
- // no validation if no min and max
- if ( getMin() == null && getMax() == null && getOptionValues() == null )
- {
- return null;
- }
-
- // min/max for strings and passwords indicates the length
- final Comparable value;
- if ( getType() == AttributeDefinition.STRING || getType() == AttributeDefinition.PASSWORD )
- {
- if ( valueString == null )
- {
- if ( isRequired() )
- {
- return VALIDATE_MISSING;
- }
-
- return ""; // accept null value
- }
-
- if ( getMin() != null )
- {
- try
- {
- if ( valueString.length() < Integer.parseInt( getMin() ) )
- {
- return VALIDATE_LESS_THAN_MINIMUM;
- }
- }
- catch ( NumberFormatException nfe )
- {
- // cannot check min length
- }
- }
-
- if ( getMax() != null )
- {
- try
- {
- if ( valueString.length() > Integer.parseInt( getMax() ) )
- {
- return VALIDATE_GREATER_THAN_MAXIMUM;
- }
- }
- catch ( NumberFormatException nfe )
- {
- // cannot check min length
- }
- }
-
- value = valueString;
- }
- else
- {
- value = convertToType( valueString );
- if ( value == null )
- {
- if ( isRequired() )
- {
- return VALIDATE_MISSING;
- }
-
- return ""; // accept null value
- }
-
- Comparable other = convertToType( getMin() );
- if ( other != null )
- {
- if ( value.compareTo( other ) < 0 )
- {
- return VALIDATE_LESS_THAN_MINIMUM;
- }
- }
-
- other = convertToType( getMax() );
- if ( other != null )
- {
- if ( value.compareTo( other ) > 0 )
- {
- return VALIDATE_GREATER_THAN_MAXIMUM;
- }
- }
- }
-
- String[] optionValues = getOptionValues();
- if ( optionValues != null && optionValues.length > 0 )
- {
- for ( int i = 0; i < optionValues.length; i++ )
- {
- Comparable other = convertToType( optionValues[i] );
- if ( value.compareTo( other ) == 0 )
- {
- // one of the option values
- return "";
- }
- }
-
- // not any of the option values, fail
- return VALIDATE_NOT_A_VALID_OPTION;
- }
-
- // finally, we accept the value
- return "";
+ return ADValidator.validate(this, valueString);
}
@@ -440,59 +344,50 @@
public static String[] splitList( String listString )
{
- // check for non-existing or empty lists
- if ( listString == null )
- {
- return null;
- }
- else if ( listString.length() == 0 )
- {
- return new String[]
- { "" };
- }
+ if (listString == null) {
+ return null;
+ } else if (listString.length() == 0) {
+ return new String[] { "" };
+ }
- List values = new ArrayList();
- boolean escape = false;
- StringBuffer buf = new StringBuffer();
- for ( int i = 0; i < listString.length(); i++ )
- {
- char c = listString.charAt( i );
+ List strings = new ArrayList();
+ StringBuffer sb = new StringBuffer();
- if ( escape )
- {
- // just go ahead
- escape = false;
- }
- else if ( c == ',' )
- {
- String value = buf.toString().trim();
- if ( value.length() > 0 )
- {
- values.add( value );
- }
- buf.delete( 0, buf.length() );
- continue;
- }
- else if ( c == '\\' )
- {
- escape = true;
- continue;
- }
+ int length = listString.length();
+ boolean escaped = false;
+
+ for (int i = 0; i < length; i++) {
+ char ch = listString.charAt(i);
+ if (ch == '\\') {
+ if (!escaped) {
+ escaped = true;
+ continue;
+ }
+ } else if (ch == ',') {
+ if (!escaped) {
+ // unescaped comma, this is a string delimiter...
+ strings.add(sb.toString());
+ sb.setLength(0);
+ continue;
+ }
+ } else if (ch == ' ') {
+ // we should ignore spaces normally, unless they are escaped...
+ if (!escaped) {
+ continue;
+ }
+ } else if (Character.isWhitespace(ch)) {
+ // Other whitespaces are ignored...
+ continue;
+ }
- buf.append( c );
- }
+ sb.append(ch);
+ escaped = false;
+ }
- // add last string
- if ( buf.length() > 0 )
- {
- String value = buf.toString().trim();
- if ( value.length() > 0 )
- {
- values.add( value );
- }
- }
+ // Always add the last string, as it contains everything after the last comma...
+ strings.add(sb.toString());
- return values.isEmpty() ? null : ( String[] ) values.toArray( new String[values.size()] );
+ return (String[]) strings.toArray(new String[strings.size()]);
}
diff --git a/metatype/src/main/java/org/apache/felix/metatype/ADValidator.java b/metatype/src/main/java/org/apache/felix/metatype/ADValidator.java
new file mode 100644
index 0000000..d82b136
--- /dev/null
+++ b/metatype/src/main/java/org/apache/felix/metatype/ADValidator.java
@@ -0,0 +1,348 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.felix.metatype;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.osgi.service.metatype.AttributeDefinition;
+
+/**
+ * Provides various validation routines used by the {@link AD#validate(String)}
+ * method.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+final class ADValidator {
+
+ /**
+ * Validates a given input string according to the type specified by the
+ * given attribute definition.
+ *
+ * @param ad
+ * the attribute definition to use in the validation;
+ * @param rawInput
+ * the raw input value to validate.
+ * @return <code>null</code> if no validation is available, <tt>""</tt> if
+ * validation was successful, or any other non-empty string in case
+ * validation fails.
+ */
+ public static String validate(AD ad, String rawInput) {
+ // Handle the case in which the given input is not defined...
+ if (rawInput == null) {
+ if (ad.isRequired()) {
+ return AD.VALIDATE_MISSING;
+ }
+
+ return ""; // accept null value...
+ }
+
+ // Raw input is defined, validate it further
+ String[] input;
+
+ if (ad.getCardinality() == 0) {
+ input = new String[] { rawInput.trim() };
+ } else {
+ input = AD.splitList(rawInput);
+ }
+
+ int type = ad.getType();
+ switch (type) {
+ case AttributeDefinition.BOOLEAN:
+ return validateBooleanValue(ad, input);
+
+ case AttributeDefinition.CHARACTER:
+ return validateCharacterValue(ad, input);
+
+ case AttributeDefinition.BIGDECIMAL:
+ case AttributeDefinition.BIGINTEGER:
+ case AttributeDefinition.BYTE:
+ case AttributeDefinition.DOUBLE:
+ case AttributeDefinition.FLOAT:
+ case AttributeDefinition.INTEGER:
+ case AttributeDefinition.LONG:
+ case AttributeDefinition.SHORT:
+ return validateNumericValue(ad, input);
+
+ case AttributeDefinition.PASSWORD:
+ case AttributeDefinition.STRING:
+ return validateString(ad, input);
+
+ default:
+ return null; // no validation present...
+ }
+ }
+
+ /**
+ * Searches for a given search value in a given array of options.
+ *
+ * @param searchValue
+ * the value to search for;
+ * @param optionValues
+ * the values to search in.
+ * @return <code>null</code> if the given search value is not found in the
+ * given options, the searched value if found, or <tt>""</tt> if no
+ * search value or options were given.
+ */
+ private static String findOptionValue(String searchValue, String[] optionValues) {
+ if ((searchValue == null) || (optionValues == null) || (optionValues.length == 0)) {
+ // indicates that we've not searched
+ return "";
+ }
+
+ for (int i = 0; i < optionValues.length; i++) {
+ if (optionValues[i].equals(searchValue)) {
+ return optionValues[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Parses a given string value into a numeric type.
+ *
+ * @param type
+ * the type to parse;
+ * @param value
+ * the value to parse.
+ * @return a {@link Number} representation of the given value, or
+ * <code>null</code> if the input was <code>null</code>, empty, or
+ * not a numeric type.
+ * @throws NumberFormatException
+ * in case the given value cannot be parsed as numeric value.
+ */
+ private static Comparable parseNumber(int type, String value) throws NumberFormatException {
+ if ((value != null) && (value.length() > 0)) {
+ switch (type) {
+ case AttributeDefinition.BIGDECIMAL:
+ return new BigDecimal(value);
+ case AttributeDefinition.BIGINTEGER:
+ return new BigInteger(value);
+ case AttributeDefinition.BYTE:
+ return Byte.valueOf(value);
+ case AttributeDefinition.SHORT:
+ return Short.valueOf(value);
+ case AttributeDefinition.INTEGER:
+ return Integer.valueOf(value);
+ case AttributeDefinition.LONG:
+ return Long.valueOf(value);
+ case AttributeDefinition.FLOAT:
+ return Float.valueOf(value);
+ case AttributeDefinition.DOUBLE:
+ return Double.valueOf(value);
+ default:
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses a given string value as character, allowing <code>null</code>
+ * -values and empty values to be given as input.
+ *
+ * @param value
+ * the value to parse as character, can be <code>null</code> or
+ * an empty value.
+ * @return the character value if, and only if, the given input was non-
+ * <code>null</code> and a non-empty string.
+ */
+ private static Character parseOptionalChar(String value) {
+ if ((value != null) && (value.length() > 0)) {
+ return Character.valueOf(value.charAt(0));
+ }
+ return null;
+ }
+
+ /**
+ * Parses a given string value as integer, allowing <code>null</code>-values
+ * and invalid numeric values to be given as input.
+ *
+ * @param value
+ * the value to parse as integer, can be <code>null</code> or a
+ * non-numeric value.
+ * @return the integer value if, and only if, the given input was non-
+ * <code>null</code> and a valid integer representation.
+ */
+ private static Integer parseOptionalInt(String value) {
+ if (value != null) {
+ try {
+ return Integer.valueOf(value);
+ } catch (NumberFormatException e) {
+ // Ignore; invalid value...
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Validates a given input string as boolean value.
+ *
+ * @param ad
+ * the attribute definition to use in the validation;
+ * @param input
+ * the array with input values to validate.
+ * @return <code>null</code> if no validation is available, <tt>""</tt> if
+ * validation was successful, or any other non-empty string in case
+ * validation fails.
+ */
+ private static String validateBooleanValue(AD ad, String[] input) {
+ for (int i = 0; i < input.length; i++) {
+ int length = input[i].length();
+ if ((length == 0) && ad.isRequired()) {
+ return AD.VALIDATE_MISSING;
+ } else if (length > 0 && !"true".equalsIgnoreCase(input[i]) && !"false".equalsIgnoreCase(input[i])) {
+ return AD.VALIDATE_INVALID_VALUE;
+ }
+ }
+
+ String[] optionValues = ad.getOptionValues();
+ if (optionValues != null && optionValues.length > 0) {
+ return null; // no validation possible for this type...
+ }
+
+ return ""; // accept given value...
+ }
+
+ /**
+ * Validates a given input string as character value.
+ *
+ * @param ad
+ * the attribute definition to use in the validation;
+ * @param input
+ * the array with input values to validate.
+ * @return <code>null</code> if no validation is available, <tt>""</tt> if
+ * validation was successful, or any other non-empty string in case
+ * validation fails.
+ */
+ private static String validateCharacterValue(AD ad, String[] input) {
+ Character min = parseOptionalChar(ad.getMin());
+ Character max = parseOptionalChar(ad.getMax());
+ String[] optionValues = ad.getOptionValues();
+
+ for (int i = 0; i < input.length; i++) {
+ Character ch = null;
+ int length = input[i].length();
+ if (length > 1) {
+ return AD.VALIDATE_GREATER_THAN_MAXIMUM;
+ } else if ((length == 0) && ad.isRequired()) {
+ return AD.VALIDATE_MISSING;
+ } else if (length == 1) {
+ ch = Character.valueOf(input[i].charAt(0));
+ // Check whether the minimum value is adhered for all values...
+ if ((min != null) && (ch.compareTo(min) < 0)) {
+ return AD.VALIDATE_LESS_THAN_MINIMUM;
+ }
+ // Check whether the maximum value is adhered for all values...
+ if ((max != null) && (ch.compareTo(max) > 0)) {
+ return AD.VALIDATE_GREATER_THAN_MAXIMUM;
+ }
+ }
+
+ if (findOptionValue(input[i], optionValues) == null) {
+ return AD.VALIDATE_NOT_A_VALID_OPTION;
+ }
+ }
+
+ return ""; // accept given value...
+ }
+
+ /**
+ * Validates a given input string as numeric value.
+ *
+ * @param ad
+ * the attribute definition to use in the validation;
+ * @param input
+ * the array with input values to validate.
+ * @return <code>null</code> if no validation is available, <tt>""</tt> if
+ * validation was successful, or any other non-empty string in case
+ * validation fails.
+ */
+ private static String validateNumericValue(AD ad, String[] input) {
+ Integer min = parseOptionalInt(ad.getMin());
+ Integer max = parseOptionalInt(ad.getMax());
+ String[] optionValues = ad.getOptionValues();
+
+ for (int i = 0; i < input.length; i++) {
+ Comparable value = null;
+ try {
+ value = parseNumber(ad.getType(), input[i]);
+ } catch (NumberFormatException e) {
+ return AD.VALIDATE_INVALID_VALUE;
+ }
+
+ if ((value == null) && ad.isRequired()) {
+ // Possible if the cardinality != 0 and input was something like
+ // "0,,1"...
+ return AD.VALIDATE_MISSING;
+ }
+ // Check whether the minimum value is adhered for all values...
+ if ((min != null) && (value != null) && (value.compareTo(min) < 0)) {
+ return AD.VALIDATE_LESS_THAN_MINIMUM;
+ }
+ // Check whether the maximum value is adhered for all values...
+ if ((max != null) && (value != null) && (value.compareTo(max) > 0)) {
+ return AD.VALIDATE_GREATER_THAN_MAXIMUM;
+ }
+
+ if (findOptionValue(input[i], optionValues) == null) {
+ return AD.VALIDATE_NOT_A_VALID_OPTION;
+ }
+ }
+
+ return ""; // accept given value...
+ }
+
+ /**
+ * Validates a given input string as string (or password).
+ *
+ * @param ad
+ * the attribute definition to use in the validation;
+ * @param input
+ * the array with input values to validate.
+ * @return <code>null</code> if no validation is available, <tt>""</tt> if
+ * validation was successful, or any other non-empty string in case
+ * validation fails.
+ */
+ private static String validateString(AD ad, String[] input) {
+ Integer min = parseOptionalInt(ad.getMin());
+ Integer max = parseOptionalInt(ad.getMax());
+ String[] optionValues = ad.getOptionValues();
+
+ for (int i = 0; i < input.length; i++) {
+ int length = input[i].length();
+ // Check whether the minimum length is adhered for all values...
+ if ((min != null) && (length < min.intValue())) {
+ return AD.VALIDATE_LESS_THAN_MINIMUM;
+ }
+ // Check whether the maximum length is adhered for all values...
+ if ((max != null) && (length > max.intValue())) {
+ return AD.VALIDATE_GREATER_THAN_MAXIMUM;
+ }
+
+ if (findOptionValue(input[i], optionValues) == null) {
+ return AD.VALIDATE_NOT_A_VALID_OPTION;
+ }
+ }
+
+ return ""; // accept given value...
+ }
+}
diff --git a/metatype/src/test/java/org/apache/felix/metatype/ADTest.java b/metatype/src/test/java/org/apache/felix/metatype/ADTest.java
index 5c4980b..065f0c9 100644
--- a/metatype/src/test/java/org/apache/felix/metatype/ADTest.java
+++ b/metatype/src/test/java/org/apache/felix/metatype/ADTest.java
@@ -78,6 +78,31 @@
}
+ public void testEmptySecond()
+ {
+ String value0 = "value0";
+ String value1 = "";
+ String listString = value0 + ",";
+ String[] list = AD.splitList( listString );
+ assertNotNull( list );
+ assertEquals( 2, list.length );
+ assertEquals( value0, list[0] );
+ assertEquals( value1, list[1] );
+ }
+
+ public void testSpacedSecond()
+ {
+ String value0 = "value0";
+ String value1 = "";
+ String listString = value0 + ", ";
+ String[] list = AD.splitList( listString );
+ assertNotNull( list );
+ assertEquals( 2, list.length );
+ assertEquals( value0, list[0] );
+ assertEquals( value1, list[1] );
+ }
+
+
public void testSingleBlanks()
{
String value0 = "value";
@@ -106,9 +131,9 @@
{
String value0 = "a,b";
String value1 = "b,c";
- String value2 = "c\\";
+ String value2 = " c\\";
String value3 = "d";
- String listString = "a\\,b,b\\,c, c\\\\,d";
+ String listString = "a\\,b,b\\,c,\\ c\\\\,d";
String[] list = AD.splitList( listString );
assertNotNull( list );
assertEquals( 4, list.length );
@@ -133,4 +158,42 @@
assertEquals( AttributeDefinition.PASSWORD, AD.toType( "Password" ) );
assertEquals( AttributeDefinition.STRING, AD.toType( "JohnDoe" ) );
}
+
+ /**
+ * FELIX-3757: if an AD has only its 'required' property set, but no
+ * min/max or option values defined, the validation still should detect
+ * empty values.
+ */
+ public void testValidateRequiredValueWithMinimalOptions() {
+ AD ad = new AD();
+ ad.setType("Integer");
+ ad.setRequired(true);
+
+ assertEquals(AD.VALIDATE_MISSING, ad.validate(null));
+ }
+
+ /**
+ * FELIX-3756: if an AD is optional, but its validate method is called
+ * with invalid data, the value is regarded missing.
+ */
+ public void testValidateOptionalValueWithInvalidData() {
+ AD ad = new AD();
+ ad.setType("Integer");
+ ad.setRequired(false);
+
+ assertEquals(AD.VALIDATE_INVALID_VALUE, ad.validate("abc"));
+ }
+
+ /**
+ * FELIX-3758: if an AD has a cardinality != 0, the validation method
+ * cannot handle a comma-separated input.
+ */
+ public void testValidateValueWithMultiValueCardinality() {
+ AD ad = new AD();
+ ad.setType("Integer");
+ ad.setCardinality(2);
+ ad.setRequired(true);
+
+ assertEquals("", ad.validate("1,2"));
+ }
}
diff --git a/metatype/src/test/java/org/apache/felix/metatype/ADValidatorTest.java b/metatype/src/test/java/org/apache/felix/metatype/ADValidatorTest.java
new file mode 100644
index 0000000..8640629
--- /dev/null
+++ b/metatype/src/test/java/org/apache/felix/metatype/ADValidatorTest.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.felix.metatype;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for {@link ADValidator}.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ADValidatorTest extends TestCase {
+
+ /**
+ * Tests the validation of boolean is only done minimally.
+ */
+ public void testValidateBoolean() {
+ AD ad = new AD();
+ ad.setType("Boolean");
+ ad.setRequired(false);
+
+ // optional value
+ assertEquals("", ADValidator.validate(ad, null));
+ // adhere minimal value
+ assertEquals("", ADValidator.validate(ad, "true"));
+ // adhere maximal value
+ assertEquals("", ADValidator.validate(ad, "false"));
+ // not a valid value
+ assertEquals(AD.VALIDATE_INVALID_VALUE, ADValidator.validate(ad, "foobar"));
+
+ ad.setCardinality(3); // up to three values are allowed...
+
+ // mandatory value
+ assertEquals("", ADValidator.validate(ad, null));
+ // 2nd value is missing
+ assertEquals("", ADValidator.validate(ad, "true,,false"));
+
+ ad.setRequired(true);
+
+ // mandatory value
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, null));
+ // 2nd value is missing
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, "false,,true"));
+ assertEquals("", ADValidator.validate(ad, "false, true, false"));
+
+ ad.setOptions(Collections.singletonMap("true", "Yes!"));
+
+ assertEquals(null, ADValidator.validate(ad, "false, true, false"));
+ }
+
+ /**
+ * Tests the validation of characters with only limited set of options.
+ */
+ public void testValidateCharacterOptionValues() {
+ AD ad = new AD();
+ ad.setType("Char");
+ ad.setRequired(false);
+
+ // optional value
+ assertEquals("", ADValidator.validate(ad, null));
+ // option too long
+ assertEquals(AD.VALIDATE_GREATER_THAN_MAXIMUM, ADValidator.validate(ad, "ab"));
+ // adhere first option value
+ assertEquals("", ADValidator.validate(ad, "b"));
+ // adhere last option value
+ assertEquals("", ADValidator.validate(ad, "e"));
+
+ ad.setCardinality(3); // up to three values are allowed...
+
+ // mandatory value
+ assertEquals("", ADValidator.validate(ad, ""));
+ // 2nd value is missing
+ assertEquals("", ADValidator.validate(ad, "b,,c"));
+
+ ad.setRequired(true);
+
+ // mandatory value
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, null));
+ // 2nd value is missing
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, "b,,c"));
+ // adhere minimal values
+ assertEquals("", ADValidator.validate(ad, "b, c, d"));
+ // adhere maximal values
+ assertEquals("", ADValidator.validate(ad, "c, d, e"));
+
+ Map options = new HashMap();
+ options.put("b", "B");
+ options.put("c", "C");
+ options.put("d", "D");
+ options.put("e", "E");
+
+ ad.setOptions(options);
+ // no option given
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, ""));
+ // invalid option
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "a"));
+ // too great
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "f"));
+ // 2nd value is too less
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "b,a,c"));
+ // 3rd value is too great
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "d, e, f"));
+
+ ad.setMin("b");
+ ad.setMax("c");
+ ad.setOptions(Collections.emptyMap());
+
+ // adhere minimal values
+ assertEquals("", ADValidator.validate(ad, "b, c, b"));
+ // d is too great
+ assertEquals(AD.VALIDATE_GREATER_THAN_MAXIMUM, ADValidator.validate(ad, "b, c, d"));
+ // a is too less
+ assertEquals(AD.VALIDATE_LESS_THAN_MINIMUM, ADValidator.validate(ad, "a, b, c"));
+ }
+
+ /**
+ * Tests the validation of characters with only limited set of options.
+ */
+ public void testValidateDoubleOptionValues() {
+ Map options = new HashMap();
+ options.put("1.1", "B");
+ options.put("2.2", "C");
+ options.put("3.3", "D");
+ options.put("4.4", "E");
+
+ AD ad = new AD();
+ ad.setType("Double");
+ ad.setOptions(options);
+ ad.setRequired(false);
+
+ // optional value
+ assertEquals("", ADValidator.validate(ad, null));
+ // invalid option
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "1.0"));
+ // adhere first option value
+ assertEquals("", ADValidator.validate(ad, "1.1"));
+ // adhere last option value
+ assertEquals("", ADValidator.validate(ad, "4.4"));
+ // too great
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "4.5"));
+
+ ad.setCardinality(3); // up to three values are allowed...
+ ad.setRequired(true);
+
+ // mandatory value
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, null));
+ // 2nd value is too less
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "1.1,1.0,2.2"));
+ // adhere minimal values
+ assertEquals("", ADValidator.validate(ad, "1.1, 2.2, 3.3"));
+ // adhere maximal values
+ assertEquals("", ADValidator.validate(ad, "2.2, 3.3, 4.4"));
+ // 3rd value is too great
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "3.3, 4.4, 5.5"));
+ }
+
+ /**
+ * Tests the validation of integers is based on the minimum and maximum values.
+ */
+ public void testValidateInteger() {
+ AD ad = new AD();
+ ad.setType("Integer");
+ ad.setMin("3"); // only values greater than 2
+ ad.setMax("6"); // only values less than 7
+ ad.setRequired(false);
+
+ // optional value
+ assertEquals("", ADValidator.validate(ad, null));
+ // too less
+ assertEquals(AD.VALIDATE_LESS_THAN_MINIMUM, ADValidator.validate(ad, "2"));
+ // adhere minimal value
+ assertEquals("", ADValidator.validate(ad, "3"));
+ // adhere maximal value
+ assertEquals("", ADValidator.validate(ad, "6"));
+ // too great
+ assertEquals(AD.VALIDATE_GREATER_THAN_MAXIMUM, ADValidator.validate(ad, "7"));
+
+ ad.setCardinality(3); // up to three values are allowed...
+
+ // mandatory value
+ assertEquals("", ADValidator.validate(ad, null));
+ // 2nd value is missing
+ assertEquals("", ADValidator.validate(ad, "3,,3"));
+
+ ad.setRequired(true);
+
+ // mandatory value
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, null));
+ // 2nd value is missing
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, "3,,3"));
+ // 2nd value is invalid
+ assertEquals(AD.VALIDATE_INVALID_VALUE, ADValidator.validate(ad, "3,a,3"));
+ // 2nd value is too less
+ assertEquals(AD.VALIDATE_LESS_THAN_MINIMUM, ADValidator.validate(ad, "3,2,3"));
+ // adhere minimal values
+ assertEquals("", ADValidator.validate(ad, "3, 4, 5"));
+ // adhere maximal values
+ assertEquals("", ADValidator.validate(ad, "6, 5, 4"));
+ // 3rd value is too great
+ assertEquals(AD.VALIDATE_GREATER_THAN_MAXIMUM, ADValidator.validate(ad, "5, 6, 7"));
+ }
+
+ /**
+ * Tests the validation of strings is based on the minimum and maximum lengths.
+ */
+ public void testValidateString() {
+ AD ad = new AD();
+ ad.setType("String");
+ ad.setRequired(false);
+
+ // optional value
+ assertEquals("", ADValidator.validate(ad, null));
+ // any length of input is accepted
+ assertEquals("", ADValidator.validate(ad, "1234567890"));
+
+ ad.setMin("3"); // minimal length == 3
+ ad.setMax("6"); // maximum length == 6
+
+ // too short
+ assertEquals(AD.VALIDATE_LESS_THAN_MINIMUM, ADValidator.validate(ad, "12"));
+ // adhere minimum length
+ assertEquals("", ADValidator.validate(ad, "123"));
+ // adhere maximum length
+ assertEquals("", ADValidator.validate(ad, "12356"));
+ // too long
+ assertEquals(AD.VALIDATE_GREATER_THAN_MAXIMUM, ADValidator.validate(ad, "1234567"));
+
+ ad.setCardinality(3); // up to three values are allowed...
+ ad.setRequired(true);
+
+ // mandatory value
+ assertEquals(AD.VALIDATE_MISSING, ADValidator.validate(ad, null));
+ // 2nd value is too short
+ assertEquals(AD.VALIDATE_LESS_THAN_MINIMUM, ADValidator.validate(ad, "321,12,123"));
+ // adhere minimum lengths
+ assertEquals("", ADValidator.validate(ad, "123, 123, 123"));
+ // adhere maximum lengths
+ assertEquals("", ADValidator.validate(ad, "12356, 654321, 123456"));
+ // 3rd value is too long
+ assertEquals(AD.VALIDATE_GREATER_THAN_MAXIMUM, ADValidator.validate(ad, "123, 123, 1234567"));
+
+ ad.setOptions(Collections.singletonMap("123", "foo"));
+
+ // adhere minimum lengths
+ assertEquals("", ADValidator.validate(ad, "123, 123, 123"));
+ assertEquals(AD.VALIDATE_NOT_A_VALID_OPTION, ADValidator.validate(ad, "2134"));
+ }
+}