001: /*
002: * Copyright 2005-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005: * in compliance with the License. You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software distributed under the License
010: * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011: * or implied. See the License for the specific language governing permissions and limitations under
012: * the License.
013: */
014:
015: package org.strecks.validator.internal;
016:
017: import java.lang.annotation.Annotation;
018: import java.lang.reflect.Method;
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.TreeMap;
024:
025: import org.strecks.bind.internal.BindConvertInfo;
026: import org.strecks.converter.Converter;
027: import org.strecks.converter.SafeBeanUtilsConverter;
028: import org.strecks.converter.handler.ConversionHandler;
029: import org.strecks.converter.handler.DefaultConversionHandler;
030: import org.strecks.exceptions.ApplicationConfigurationException;
031: import org.strecks.util.ReflectHelper;
032: import org.strecks.validator.Validator;
033: import org.strecks.validator.annotation.ValidatorFactoryClass;
034: import org.strecks.validator.factory.ValidatorFactory;
035: import org.strecks.validator.strategy.DefaultValidationStrategy;
036:
037: /**
038: * Looks for <code>ValidatorFactory</code> in the <code>ValidBindingForm</code> form bean. Identifies these as
039: * annotations which themselves are annotated using the <code>ValidatorFactoryClass</code> annotation
040: * @author Phil Zoio
041: */
042: public class ValidationAnnotationReader {
043:
044: /**
045: * @param bean
046: * the bean being validated
047: * @param info
048: * contains the bind handlers and converters for the bean
049: */
050: public ValidationInfo readValidationHandlers(Object bean,
051: BindConvertInfo info) {
052:
053: Class this Class = bean.getClass();
054:
055: Map<OrderedProperty, MethodValidators> map = new TreeMap<OrderedProperty, MethodValidators>();
056:
057: for (Method method : this Class.getMethods()) {
058:
059: String setterName = method.getName();
060: String propertyName = ReflectHelper
061: .getPropertyName(setterName);
062: Converter converter = getConverter(this Class, propertyName,
063: info);
064:
065: MethodValidators methodValidators = readMethodAnnotations(
066: method, propertyName, converter);
067: if (methodValidators != null)
068: map.put(methodValidators.getOrderedProperty(),
069: methodValidators);
070:
071: }
072:
073: ConversionHandler conversionHandler = getConversionHandler(info);
074:
075: ValidationInfo vi = new ValidationInfo(map,
076: new DefaultValidationStrategy(), conversionHandler);
077: return vi;
078: }
079:
080: MethodValidators readMethodAnnotations(Method method,
081: String propertyName, Converter converter) {
082:
083: List<ValidatorWrapper> validators = null;
084:
085: Annotation[] annotations = method.getAnnotations();
086:
087: int minOrder = 999;
088:
089: boolean foundValidator = false;
090:
091: for (Annotation annotation : annotations) {
092:
093: Class<? extends Annotation> annotationType = annotation
094: .annotationType();
095: ValidatorFactoryClass factory = annotationType
096: .getAnnotation(ValidatorFactoryClass.class);
097:
098: if (factory != null) {
099:
100: checkIsSetter(method);
101:
102: foundValidator = true;
103:
104: Class validatorFactoryClass = factory.value();
105:
106: ValidatorFactory v = ReflectHelper.createInstance(
107: validatorFactoryClass, ValidatorFactory.class);
108:
109: Method getter = getGetter(method);
110: ValidatorWrapper wrapper = v.create(annotation, getter);
111: Validator validator = wrapper.getValidator();
112:
113: // if we using the raw type, we can check that the generic type matches the
114: if (!wrapper.getUsesConvertedValue()) {
115: checkValidatorType(propertyName, getter, validator
116: .getClass(), Validator.class,
117: annotationType);
118: }
119:
120: validators = addValidator(validators, wrapper);
121:
122: minOrder = updateMinOrder(minOrder, wrapper);
123:
124: }
125:
126: // sort the validators internally according to order
127: if (foundValidator)
128: Collections.sort(validators);
129: }
130:
131: if (validators != null) {
132: OrderedProperty orderedProperty = new OrderedProperty(
133: propertyName, minOrder);
134: return newMethodValidators(method.getDeclaringClass(),
135: orderedProperty, validators, converter);
136: } else
137: return null;
138:
139: }
140:
141: ConversionHandler getConversionHandler(BindConvertInfo info) {
142: ConversionHandler conversionHandler = null;
143:
144: if (info != null) {
145: conversionHandler = info.getConversionHandler();
146: } else {
147: conversionHandler = new DefaultConversionHandler();
148: }
149: return conversionHandler;
150: }
151:
152: List<ValidatorWrapper> addValidator(
153: List<ValidatorWrapper> validators, ValidatorWrapper wrapper) {
154: if (validators == null) {
155: validators = new ArrayList<ValidatorWrapper>();
156: }
157: validators.add(wrapper);
158: return validators;
159: }
160:
161: private int updateMinOrder(int minOrder, ValidatorWrapper wrapper) {
162: minOrder = Math.min(wrapper.getOrder(), minOrder);
163: return minOrder;
164: }
165:
166: MethodValidators newMethodValidators(Class<?> declaringClass,
167: OrderedProperty orderedProperty,
168: List<ValidatorWrapper> validators, Converter converter) {
169: ConvertedTypeValidationChecker checker = new ConvertedTypeValidationChecker(
170: declaringClass, orderedProperty, validators);
171: boolean usesConvertedValue = checker.checkConvertedValueTypes();
172:
173: Class<?> converterType = null;
174: if (usesConvertedValue) {
175: converterType = checker.getConverterType();
176: converter.setTargetClass(converterType);
177: }
178: return new MethodValidators(orderedProperty, validators,
179: converter, usesConvertedValue, converterType);
180: }
181:
182: Converter getConverter(Class this Class, String propertyName,
183: BindConvertInfo info) {
184: Converter converter = null;
185: if (info != null) {
186: Map<String, Converter> converterMap = info
187: .getConverterMap();
188:
189: if (converterMap != null)
190: converter = converterMap.get(propertyName);
191:
192: }
193: if (converter == null) {
194: //log.info("No converter available for property name " + propertyName + " in class " + thisClass.getName()
195: // + ". Validation may not work correctly if converting validator is not present");
196: converter = new SafeBeanUtilsConverter();
197: }
198: return converter;
199: }
200:
201: Method getGetter(Method method) {
202: Method getter = ReflectHelper.getGetter(method);
203:
204: if (getter == null) {
205: throw new ApplicationConfigurationException(
206: "Setter method " + method
207: + " has no corresponding getter method");
208: }
209: return getter;
210: }
211:
212: void checkValidatorType(String propertyName, Method getter,
213: Class validatorClass, Class interfaceClass,
214: Class<? extends Annotation> annotationType) {
215:
216: boolean isTypeCompatible = ReflectHelper.checkGenericType(
217: validatorClass, interfaceClass, getter.getReturnType());
218:
219: if (!isTypeCompatible) {
220: Class genericClass = ReflectHelper.getGenericType(
221: validatorClass, interfaceClass);
222:
223: throw new ApplicationConfigurationException("Class "
224: + getter.getDeclaringClass().getName()
225: + " has property " + propertyName
226: + " whose return type "
227: + getter.getReturnType().getName()
228: + " does not match the type "
229: + genericClass.getName()
230: + " of the validator used by annotation @"
231: + annotationType.getSimpleName());
232:
233: }
234:
235: }
236:
237: private void checkIsSetter(Method method) {
238: if (!ReflectHelper.isSetter(method)) {
239: throw new ApplicationConfigurationException(
240: "Invalid Validation annotation for method "
241: + method
242: + ": annotation only supports setter properties");
243: }
244: }
245:
246: }
|