org.springmodules.validation.bean.conf.loader.annotation.AnnotationBeanValidationConfigurationLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.springmodules.validation.bean.conf.loader.annotation.AnnotationBeanValidationConfigurationLoader.java

Source

/*
 * Copyright 2004-2005 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.springmodules.validation.bean.conf.loader.annotation;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springmodules.validation.bean.conf.BeanValidationConfiguration;
import org.springmodules.validation.bean.conf.DefaultBeanValidationConfiguration;
import org.springmodules.validation.bean.conf.MutableBeanValidationConfiguration;
import org.springmodules.validation.bean.conf.loader.BeanValidationConfigurationLoader;
import org.springmodules.validation.bean.conf.loader.annotation.handler.ClassValidationAnnotationHandler;
import org.springmodules.validation.bean.conf.loader.annotation.handler.PropertyValidationAnnotationHandler;

/**
 * A {@link org.springmodules.validation.bean.conf.loader.BeanValidationConfigurationLoader} implementation that creates validation configuration based on
 * validation rule extracted from class annoatations.
 *
 * @author Uri Boness
 */
public class AnnotationBeanValidationConfigurationLoader implements BeanValidationConfigurationLoader {

    private final static Log logger = LogFactory.getLog(AnnotationBeanValidationConfigurationLoader.class);

    private boolean checkValidatableAnnotation;

    private Map<Class, BeanValidationConfiguration> configurationByClass;

    private ValidationAnnotationHandlerRegistry handlerRegistry;

    /**
     * Constructs a new AnnotationBeanValidationConfigurationLoader. This loader supports all classes by default.
     */
    public AnnotationBeanValidationConfigurationLoader() {
        this(false);
    }

    /**
     * Constructs a new AnnotationBeanValidationConfigurationLoader. if <code>checkValidatableAnnotation</code> is set
     * to <code>true</code>, only classes annotated with the {@link @Validatable} annoation are supported.
     *
     * @param checkValidatableAnnotation Indicates whether this loader should only support {@link @Validatable}
     *        annotated classes.
     */
    public AnnotationBeanValidationConfigurationLoader(boolean checkValidatableAnnotation) {
        this.checkValidatableAnnotation = checkValidatableAnnotation;
        configurationByClass = new HashMap<Class, BeanValidationConfiguration>();
        handlerRegistry = new DefaultValidationAnnotationHandlerRegistry();
    }

    /**
     * If {@link #isCheckValidatableAnnotation()} return <code>true</code>, then this loader supports the given
     * class only if it has the {@link @Validatable} annotation. Otherwise, all classes are supported.
     *
     * @see BeanValidationConfigurationLoader#supports(Class)
     */
    public boolean supports(Class clazz) {
        return (checkValidatableAnnotation) ? clazz.isAnnotationPresent(Validatable.class) : true;
    }

    /**
     * Loads the validation configuration for the given class based on validation annotations. The resolved configuration
     * are cached after the first time they are being loaded.
     *
     * @see BeanValidationConfigurationLoader#loadConfiguration(Class)
     */
    public BeanValidationConfiguration loadConfiguration(Class clazz) {
        BeanValidationConfiguration configuration = configurationByClass.get(clazz);
        if (configuration == null) {
            configuration = createValidationConfiguration(clazz);
            configurationByClass.put(clazz, configuration);
        }
        return configuration;
    }

    //=============================================== Setter/Getter ====================================================

    /**
     * Sets whether this loader should only support classes that are marked with the {@link @Validatable} annotation.
     *
     * @param checkValidatableAnnotation determine whether {@link @Validatable} should be considered.
     */
    public void setCheckValidatableAnnotation(boolean checkValidatableAnnotation) {
        this.checkValidatableAnnotation = checkValidatableAnnotation;
    }

    /**
     * Returns whether this loader only supports classes that are marked with the {@link @Validatable} annotation.
     *
     * @return <code>true</code> if the {@link @Validatable} is considered, <code>false</code> otherwise.
     */
    public boolean isCheckValidatableAnnotation() {
        return checkValidatableAnnotation;
    }

    /**
     * Sets the validation annotation handler registry this loader will use to find the proper handlers for the
     * validation annotations.
     *
     * @param handlerRegistry The validation annotation registry to be used by this loader.
     */
    public void setHandlerRegistry(ValidationAnnotationHandlerRegistry handlerRegistry) {
        this.handlerRegistry = handlerRegistry;
    }

    //=============================================== Helper Methods ===================================================

    /**
     * Creates a new {@link BeanValidationConfiguration} for the given class based on the validation annotations it
     * has.
     *
     * @param clazz The class to which the validation configuration should be created.
     * @return The newly created {@link BeanValidationConfiguration}.
     */
    protected BeanValidationConfiguration createValidationConfiguration(Class clazz) {
        MutableBeanValidationConfiguration configuration = new DefaultBeanValidationConfiguration();
        handleClassAnnotations(clazz, configuration);
        handlePropertyAnnotations(clazz, configuration);
        return configuration;
    }

    /**
     * Handles all the class level annotation of the given class and manipulates the given validation configuration
     * accordingly.
     *
     * @param clazz The annotated class.
     * @param configuration The bean validation configuration to manipulate.
     */
    protected void handleClassAnnotations(Class clazz, MutableBeanValidationConfiguration configuration) {
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            ClassValidationAnnotationHandler handler = handlerRegistry.findClassHanlder(annotation, clazz);
            if (handler == null) {
                logger.warn("No hanlder is defined for annotation '" + annotation.annotationType().getName()
                        + "'... Annotation will be ignored...");
            } else {
                handler.handleAnnotation(annotation, clazz, configuration);
            }
        }
    }

    /**
     * Handles all the property level annotations of the given class and manipulates the given validation configuration
     * accordingly. The property level annotations can either be placed on the <code>setter</code> methods of the
     * properties or on the appropriate class fields.
     *
     * @param clazz The annotated class.
     * @param configuration The bean validation configuration to mainpulate.
     */
    protected void handlePropertyAnnotations(Class clazz, MutableBeanValidationConfiguration configuration) {
        // extracting & handling all property annotation placed on property fields
        List<Field> fields = extractFieldFromClassHierarchy(clazz);
        for (Field field : fields) {
            String fieldName = field.getName();
            PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(clazz, fieldName);
            if (descriptor != null) {
                Annotation[] annotations = field.getAnnotations();
                handleProprtyAnnotations(annotations, clazz, descriptor, configuration);
            }
        }

        // extracting & handling all property annotations placed on property setters
        PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz);
        for (PropertyDescriptor descriptor : descriptors) {
            Method writeMethod = descriptor.getWriteMethod();
            if (writeMethod == null) {
                continue;
            }
            Annotation[] annotations = writeMethod.getAnnotations();
            handleProprtyAnnotations(annotations, clazz, descriptor, configuration);
        }
    }

    /**
     * Extracts all fields in the class hierarchy of the given class.
     *
     * @param clazz The given class.
     * @return All fields in the class hierarchy of the given class.
     */
    protected List<Field> extractFieldFromClassHierarchy(Class clazz) {
        List<Field> fields = new ArrayList<Field>();
        while (clazz != null) {
            CollectionUtils.addAll(fields, clazz.getDeclaredFields());
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    /**
     * Handles all the validation annotations of the given property.
     *
     * @param annotations The annotation to handle.
     * @param validatedClass The annotated class.
     * @param descriptor The property descriptor of the annotated property.
     * @param configuration The bean validation configuration to manipulate.
     */
    protected void handleProprtyAnnotations(Annotation[] annotations, Class validatedClass,
            PropertyDescriptor descriptor, MutableBeanValidationConfiguration configuration) {
        for (Annotation annotation : annotations) {
            PropertyValidationAnnotationHandler handler = handlerRegistry.findPropertyHanlder(annotation,
                    validatedClass, descriptor);
            if (handler != null) {
                handler.handleAnnotation(annotation, validatedClass, descriptor, configuration);
            }
        }
    }

}