| /* |
| * 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. |
| * <p> |
| * The validation is done in the following way: |
| * </p> |
| * <ul> |
| * <li>If the input is undefined (ie. <code>null</code>), and the attribute is mandatory, the |
| * validation fails due to a missing value. If the attribute is optional, the input is |
| * accepted;</li> |
| * <li>If the input represents a <em>boolean</em> value, it is tested whether it is defined (in |
| * case of non-zero cardinality) and represents either <tt>"true"</tt> or <tt>"false"</tt>. The |
| * minimum and maximum parameters are <b>not</b> used in this validation;</li> |
| * <li>If the input represents a <em>character</em> value, it is tested whether it is defined |
| * (in case of non-zero cardinality). The character value must be defined within the character |
| * range specified by the minimum and maximum parameters (if defined);</li> |
| * <li>If the input represents a <em>numeric</em> value, it is tested whether it is defined (in |
| * case of non-zero cardinality). The numeric value must be defined within the numeric range |
| * specified by the minimum and maximum parameters (if defined);</li> |
| * <li>If the input represents a <em>string</em> or <em>password</em>, it is tested whether it |
| * is defined (in case of non-zero cardinality). The length of the string value must be in the |
| * range specified by the minimum and maximum parameters (if defined).</li> |
| * </ul> |
| * <p> |
| * For all types of attributes, if it defines option values, the input should be present as one |
| * of the defined option values. |
| * </p> |
| * |
| * @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 numeric value, allowing |
| * <code>null</code>-values and invalid numeric values to be given as |
| * input. |
| * |
| * @param type the type of number, should only be a numeric type; |
| * @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 Comparable parseOptionalNumber(int type, String value) |
| { |
| if (value != null) |
| { |
| try |
| { |
| return parseNumber(type, 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++) |
| { |
| String value = input[i]; |
| int length = (value == null) ? 0 : value.length(); |
| |
| if ((length == 0) && ad.isRequired()) |
| { |
| return AD.VALIDATE_MISSING; |
| } |
| else if (length > 0 && !"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value)) |
| { |
| 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] == null) ? 0 : 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) |
| { |
| Comparable min = parseOptionalNumber(ad.getType(), ad.getMin()); |
| Comparable max = parseOptionalNumber(ad.getType(), 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) |
| { |
| // The length() method of a string yields an Integer, so the maximum string length is 2^31-1... |
| Integer min = (Integer) parseOptionalNumber(AttributeDefinition.INTEGER, ad.getMin()); |
| Integer max = (Integer) parseOptionalNumber(AttributeDefinition.INTEGER, ad.getMax()); |
| String[] optionValues = ad.getOptionValues(); |
| |
| for (int i = 0; i < input.length; i++) |
| { |
| String value = input[i]; |
| final int length = value == null ? 0 : value.length(); |
| if (ad.isRequired() && value == null) |
| { |
| return AD.VALIDATE_MISSING; |
| } |
| // 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(value, optionValues) == null) |
| { |
| return AD.VALIDATE_NOT_A_VALID_OPTION; |
| } |
| } |
| |
| return ""; // accept given value... |
| } |
| } |