All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.runtime.validator.XWorkBeanValidator Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * *##% 
 * JAXX Runtime
 * Copyright (C) 2008 - 2009 CodeLutin
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * ##%*
 */
package jaxx.runtime.validator;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ValidationAwareSupport;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationManager;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
import com.opensymphony.xwork2.validator.FieldValidator;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.Validator;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * A customized validator for a given bean.
 *
 * Use the method {@link #validate(java.lang.Object)} to obtain the messages
 * detected by the validator for the given bean.
 * 
 * @param  type of the bean to validate.
 * 
 * @author chemit
 * @since 1.3
 */
public class XWorkBeanValidator {

    /** to use log facility, just put in your code: log.info(\"...\"); */
    private static final Log log = LogFactory.getLog(XWorkBeanValidator.class);
    protected final static Map> EMPTY_RESULT = Collections.unmodifiableMap(new HashMap>());
    /** the type of bean to validate */
    protected final Class beanClass;
    /** the validation named context (can be null) */
    protected String contextName;
    /** the list of field names detected for this validator */
    protected Set fieldNames;
    /** a flag to include or not the default context validators */
    protected boolean includeDefaultContext;
    // --
    // XWorks fields
    // --
    protected ValidationAwareSupport validationSupport;
    protected DelegatingValidatorContext validationContext;
    protected transient ActionValidatorManager validator;
    protected ActionContext context;

    public XWorkBeanValidator(Class beanClass, String contextName) {
        this(beanClass, contextName, true, BeanValidatorUtil.getSharedValueStack());
    }

    public XWorkBeanValidator(Class beanClass, String contextName, ValueStack vs) {
        this(beanClass, contextName, true, vs);
    }

    public XWorkBeanValidator(Class beanClass, String contextName, boolean includeDefaultContext) {
        this(beanClass, contextName, includeDefaultContext, BeanValidatorUtil.getSharedValueStack());
    }

    public XWorkBeanValidator(Class beanClass, String contextName, boolean includeDefaultContext, ValueStack vs) {

        this.beanClass = beanClass;
        this.includeDefaultContext = includeDefaultContext;
        validationSupport = new ValidationAwareSupport();
        validationContext = new DelegatingValidatorContext(validationSupport);

        if (vs == null) {
            // create a standalone value stack
            ConfigurationManager confManager = new ConfigurationManager();
            Configuration conf = confManager.getConfiguration();
            vs = conf.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            if (log.isDebugEnabled()) {
                log.info("create a standalone value stack " + vs);
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("use given value stack " + vs);
            }
        }

        context = new ActionContext(vs.getContext());
        ActionContext.setContext(context);

        // init validator
        validator = context.getContainer().getInstance(ActionValidatorManager.class, "no-annotations");

        // init context
        setContextName(contextName);
    }

    public boolean isIncludeDefaultContext() {
        return includeDefaultContext;
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public String getContextName() {
        return contextName;
    }

    public Set getFieldNames() {
        return fieldNames;
    }

    public ActionValidatorManager getValidator() {
        return validator;
    }

    /**
     * Test a the validator contains the field given his name
     *
     * @param fieldName the name of the searched field
     * @return true if validator contaisn this field, false otherwise
     */
    public boolean containsField(String fieldName) {
        return fieldNames.contains(fieldName);
    }

    public void setIncludeDefaultContext(boolean includeDefaultContext) {
        this.includeDefaultContext = includeDefaultContext;
        if (contextName != null) {
            // reload context
            setContextName(contextName);
        }
    }

    public void setContextName(String contextName) {
        this.contextName = contextName;
        // changing contextName may change fields definition
        // so reload fields
        initFields();
    }

    /**
     * Valide le bean donné et retourne les messages produits.
     *
     * @param bean le bean a valider (il doit etre non null)
     * 
     * @return le dictionnaire des messages produits par la validation indexées
     *         par le nom du champs du bean impacté.
     */
    public Map> validate(B bean) {

        if (bean == null) {
            throw new NullPointerException("bean can not be null in method validate");
        }

        Map> result = EMPTY_RESULT;

        // on lance la validation uniquement si des champs sont a valider
        if (!fieldNames.isEmpty()) {

            try {

                //TC - 20081024 : since context is in a ThreadLocal variable, we must do the check
                if (ActionContext.getContext() == null) {
                    ActionContext.setContext(context);
                }

                validator.validate(bean, contextName, validationContext);

                if (log.isTraceEnabled()) {
                    log.trace("Action errors: " + validationContext.getActionErrors());
                    log.trace("Action messages: " + validationContext.getActionMessages());
                    log.trace("Field errors: " + validationContext.getFieldErrors());
                }

                if (log.isDebugEnabled()) {
                    log.debug(this + " : " + validationContext.getFieldErrors());
                }

                if (validationContext.hasFieldErrors()) {
                    Map messages = validationContext.getFieldErrors();
                    result = new HashMap>(messages.size());
                    for (Object fieldName : messages.keySet()) {
                        Collection c = (Collection) messages.get(fieldName);
                        List mm = new java.util.ArrayList(c.size());
                        for (Object message : c) {
                            mm.add(message + "");
                        }
                        result.put(fieldName + "", mm);
                    }
                }

            } catch (ValidationException eee) {
                log.warn("Error during validation on " + beanClass + " for reason : " + eee.getMessage(), eee);

            } finally {
                // on nettoye toujours le validateur apres operation
                validationSupport.clearErrorsAndMessages();
            }
        }

        return result;
    }

    @Override
    public String toString() {
        return super.toString() + "";
    }

    /**
     * update the property {@link #fieldNames}, says search in XWorks
     */
    protected synchronized void initFields() {

        if (fieldNames != null) {
            fieldNames = null;
        }

        Set detectedFieldNames = new java.util.HashSet();

        int skip = 0;
        if (contextName != null && !includeDefaultContext) {
            // count the number of validator to skip
            for (Validator v : validator.getValidators(beanClass, null)) {
                // we only work on FieldValidator at the moment
                if (v instanceof FieldValidator) {
                    skip++;
                }
            }
        }

        for (Validator v : validator.getValidators(beanClass, contextName)) {
            // we only work on FieldValidator at the moment
            if (v instanceof FieldValidator) {
                if (skip > 0) {
                    skip--;
                    continue;
                }
                FieldValidator fieldValidator = (FieldValidator) v;
                log.debug("context " + contextName + " - field " + fieldValidator.getFieldName());
                String fName = fieldValidator.getFieldName();
                detectedFieldNames.add(fName);
            }
        }

        fieldNames = Collections.unmodifiableSet(detectedFieldNames);
    }
}