/*
* Copyright 2005-2006 the original author or authors.
*
* 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 org.strecks.validator.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.strecks.bind.internal.BindConvertInfo;
import org.strecks.converter.Converter;
import org.strecks.converter.SafeBeanUtilsConverter;
import org.strecks.converter.handler.ConversionHandler;
import org.strecks.converter.handler.DefaultConversionHandler;
import org.strecks.exceptions.ApplicationConfigurationException;
import org.strecks.util.ReflectHelper;
import org.strecks.validator.Validator;
import org.strecks.validator.annotation.ValidatorFactoryClass;
import org.strecks.validator.factory.ValidatorFactory;
import org.strecks.validator.strategy.DefaultValidationStrategy;
/**
* Looks for <code>ValidatorFactory</code> in the <code>ValidBindingForm</code> form bean. Identifies these as
* annotations which themselves are annotated using the <code>ValidatorFactoryClass</code> annotation
* @author Phil Zoio
*/
public class ValidationAnnotationReader
{
/**
* @param bean
* the bean being validated
* @param info
* contains the bind handlers and converters for the bean
*/
public ValidationInfo readValidationHandlers(Object bean, BindConvertInfo info)
{
Class thisClass = bean.getClass();
Map<OrderedProperty, MethodValidators> map = new TreeMap<OrderedProperty, MethodValidators>();
for (Method method : thisClass.getMethods())
{
String setterName = method.getName();
String propertyName = ReflectHelper.getPropertyName(setterName);
Converter converter = getConverter(thisClass, propertyName, info);
MethodValidators methodValidators = readMethodAnnotations(method, propertyName, converter);
if (methodValidators != null)
map.put(methodValidators.getOrderedProperty(), methodValidators);
}
ConversionHandler conversionHandler = getConversionHandler(info);
ValidationInfo vi = new ValidationInfo(map, new DefaultValidationStrategy(), conversionHandler);
return vi;
}
MethodValidators readMethodAnnotations(Method method, String propertyName, Converter converter)
{
List<ValidatorWrapper> validators = null;
Annotation[] annotations = method.getAnnotations();
int minOrder = 999;
boolean foundValidator = false;
for (Annotation annotation : annotations)
{
Class<? extends Annotation> annotationType = annotation.annotationType();
ValidatorFactoryClass factory = annotationType.getAnnotation(ValidatorFactoryClass.class);
if (factory != null)
{
checkIsSetter(method);
foundValidator = true;
Class validatorFactoryClass = factory.value();
ValidatorFactory v = ReflectHelper.createInstance(validatorFactoryClass, ValidatorFactory.class);
Method getter = getGetter(method);
ValidatorWrapper wrapper = v.create(annotation, getter);
Validator validator = wrapper.getValidator();
// if we using the raw type, we can check that the generic type matches the
if (!wrapper.getUsesConvertedValue())
{
checkValidatorType(propertyName, getter, validator.getClass(), Validator.class, annotationType);
}
validators = addValidator(validators, wrapper);
minOrder = updateMinOrder(minOrder, wrapper);
}
// sort the validators internally according to order
if (foundValidator)
Collections.sort(validators);
}
if (validators != null)
{
OrderedProperty orderedProperty = new OrderedProperty(propertyName, minOrder);
return newMethodValidators(method.getDeclaringClass(), orderedProperty, validators, converter);
}
else
return null;
}
ConversionHandler getConversionHandler(BindConvertInfo info)
{
ConversionHandler conversionHandler = null;
if (info != null)
{
conversionHandler = info.getConversionHandler();
}
else
{
conversionHandler = new DefaultConversionHandler();
}
return conversionHandler;
}
List<ValidatorWrapper> addValidator(List<ValidatorWrapper> validators, ValidatorWrapper wrapper)
{
if (validators == null)
{
validators = new ArrayList<ValidatorWrapper>();
}
validators.add(wrapper);
return validators;
}
private int updateMinOrder(int minOrder, ValidatorWrapper wrapper)
{
minOrder = Math.min(wrapper.getOrder(), minOrder);
return minOrder;
}
MethodValidators newMethodValidators(Class<?> declaringClass, OrderedProperty orderedProperty,
List<ValidatorWrapper> validators, Converter converter)
{
ConvertedTypeValidationChecker checker = new ConvertedTypeValidationChecker(declaringClass, orderedProperty,
validators);
boolean usesConvertedValue = checker.checkConvertedValueTypes();
Class<?> converterType = null;
if (usesConvertedValue)
{
converterType = checker.getConverterType();
converter.setTargetClass(converterType);
}
return new MethodValidators(orderedProperty, validators, converter, usesConvertedValue, converterType);
}
Converter getConverter(Class thisClass, String propertyName, BindConvertInfo info)
{
Converter converter = null;
if (info != null)
{
Map<String, Converter> converterMap = info.getConverterMap();
if (converterMap != null)
converter = converterMap.get(propertyName);
}
if (converter == null)
{
//log.info("No converter available for property name " + propertyName + " in class " + thisClass.getName()
// + ". Validation may not work correctly if converting validator is not present");
converter = new SafeBeanUtilsConverter();
}
return converter;
}
Method getGetter(Method method)
{
Method getter = ReflectHelper.getGetter(method);
if (getter == null)
{
throw new ApplicationConfigurationException("Setter method " + method
+ " has no corresponding getter method");
}
return getter;
}
void checkValidatorType(String propertyName, Method getter, Class validatorClass, Class interfaceClass,
Class<? extends Annotation> annotationType)
{
boolean isTypeCompatible = ReflectHelper.checkGenericType(validatorClass, interfaceClass, getter
.getReturnType());
if (!isTypeCompatible)
{
Class genericClass = ReflectHelper.getGenericType(validatorClass, interfaceClass);
throw new ApplicationConfigurationException("Class " + getter.getDeclaringClass().getName()
+ " has property " + propertyName + " whose return type " + getter.getReturnType().getName()
+ " does not match the type " + genericClass.getName() + " of the validator used by annotation @"
+ annotationType.getSimpleName());
}
}
private void checkIsSetter(Method method)
{
if (!ReflectHelper.isSetter(method))
{
throw new ApplicationConfigurationException("Invalid Validation annotation for method " + method
+ ": annotation only supports setter properties");
}
}
}