/** * Applies the global validation rules as listed in the given validation configuration on the * given object, and registering all global validation errors with the given {@link Errors}. * * @param configuration The bean validation configuration that holds all the global validation * rules. * @param obj The validated object. * @param errors The {@link Errors} instance where all global validation errors will be * registered. */ protected void applyGlobalValidationRules( BeanValidationConfiguration configuration, Object obj, Errors errors) { ValidationRule[] globalRules = configuration.getGlobalRules(); for (int i = 0; i < globalRules.length; i++) { ValidationRule rule = globalRules[i]; if (rule.isApplicable(obj) && !rule.getCondition().check(obj)) { String errorCode = errorCodeConverter.convertGlobalErrorCode(rule.getErrorCode(), obj.getClass()); // if there is a nested path in errors, the global errors should be registered as field // errors // for the nested path. Otherwise, they should be registered as global errors. Starting from // Spring 2.0-rc2 // this is actually not required - it's just enough to call rejectValue() with null as the // field name, // but we keep this implementation for now to support earlier versions. if (StringUtils.hasLength(errors.getNestedPath())) { String nestedPath = errors.getNestedPath(); String propertyName = nestedPath.substring(0, nestedPath.length() - 1); errors.popNestedPath(); errors.rejectValue( propertyName, errorCode, rule.getErrorArguments(obj), rule.getDefaultErrorMessage()); errors.pushNestedPath(propertyName); } else { errors.reject(errorCode, rule.getErrorArguments(obj), rule.getDefaultErrorMessage()); } } } }
/** * Applies the custom validator of the given configuration (if one exists) on the given object. * * @param configuration The configuration from which the custom validator will be taken from. * @param obj The object to be validated. * @param errors The {@link Errors} instance where all validation errors will be registered. */ protected void applyCustomValidator( BeanValidationConfiguration configuration, Object obj, Errors errors) { Validator validator = configuration.getCustomValidator(); if (validator != null) { if (validator.supports(obj.getClass())) { validator.validate(obj, errors); } } }
/** * Applies the property validation rules as listed in the given validation configuration on the * given object, and registering all property validation errors with the given {@link Errors}. * * @param configuration The bean validation configuration that holds all the property validation * rules. * @param obj The validated object. * @param errors The {@link Errors} instance where all property validation errors will be * registered (see {@link Errors#rejectValue(String, String)}). */ protected void applyPropertiesValidationRules( BeanValidationConfiguration configuration, Object obj, Errors errors) { String[] propertyNames = configuration.getValidatedProperties(); for (int i = 0; i < propertyNames.length; i++) { String propertyName = propertyNames[i]; if (logger.isDebugEnabled()) { logger.debug("Validating property '" + propertyName + "' rules..."); } ValidationRule[] rules = configuration.getPropertyRules(propertyName); // only allow one error to be associated with a property at once. This is to prevent // situations where // dependent validation rules will fail. An example can be a "minLength()" validation rule // that is dependent // on "notNull()" rule (there is not length to a null value), in this case, if the "notNull()" // rule // produces an error, the "minLength()" rule should not be applied. validateAndShortCircuitRules(rules, propertyName, obj, errors); } }
/** * The heart of this validator. This is a recursive method that validates the given object * (object) under the context of the given object graph root (root). The validation rules to be * applied are loaded using the configured {@link * org.springmodules.validation.bean.conf.loader.BeanValidationConfigurationLoader}. All errors * are registered with the given {@link Errors} object under the context of the object graph root. * * @param root The root of the object graph. * @param obj The object to be validated * @param errors The {@link Errors} instance where the validation errors will be registered. * @param validatedObjects keeps track of all the validated objects (to prevent revalidating the * same objects when a circular relationship exists). */ protected void validateObjectGraphConstraints( Object root, Object obj, Errors errors, Set validatedObjects) { // cannot load any validation rules for null values if (obj == null) { return; } // if this object was already validated, the skipping this valiation. if (validatedObjects.contains(obj)) { if (logger.isDebugEnabled()) { logger.debug( "Skipping validation of object in path '" + errors.getObjectName() + "' for it was already validated"); } return; } if (logger.isDebugEnabled()) { logger.debug("Validating object in path '" + errors.getNestedPath() + "'"); } // loading the bean validation configuration based on the validated object class. Class clazz = obj.getClass(); BeanValidationConfiguration configuration = configurationLoader.loadConfiguration(clazz); if (configuration == null) { return; // no validation configuration for this object, then there's nothing to validate. } // applying all the validation rules for the object and registering the object as "validated" applyBeanValidation(configuration, obj, errors); validatedObjects.add(obj); // after all the validation rules where applied, checking what properties of the object require // their own // validation and recursively calling this method on them. CascadeValidation[] cascadeValidations = configuration.getCascadeValidations(); BeanWrapper wrapper = wrapBean(obj); for (int i = 0; i < cascadeValidations.length; i++) { CascadeValidation cascadeValidation = cascadeValidations[i]; Condition applicabilityCondition = cascadeValidation.getApplicabilityCondition(); if (!applicabilityCondition.check(obj)) { continue; } String propertyName = cascadeValidation.getPropertyName(); Class propertyType = wrapper.getPropertyType(propertyName); Object propertyValue = wrapper.getPropertyValue(propertyName); // if the property value is not there nothing to validate. if (propertyValue == null) { continue; } // if the property is an array of a collection, then iterating on it and validating each // element. Note that // the error codes that are registered for arrays/collection elements follow the pattern // supported by // spring's PropertyAccessor. Also note that just before each recursive call, the context of // the validation // is appropriately adjusted using errors.pushNestedPath(...), and after each call it is being // adjusted back // using errors.popNestedPath(). if (propertyType.isArray()) { validateArrayProperty(root, propertyValue, propertyName, errors, validatedObjects); } else if (List.class.isAssignableFrom(propertyType) || Set.class.isAssignableFrom(propertyType)) { validateListOrSetProperty( root, (Collection) propertyValue, propertyName, errors, validatedObjects); } else if (Map.class.isAssignableFrom(propertyType)) { validateMapProperty(root, ((Map) propertyValue), propertyName, errors, validatedObjects); } else { // if the object is just a normal object (not an array or a collection), then applying its // validation rules. validatedSubBean(root, propertyValue, propertyName, errors, validatedObjects); } } }