Package org.richfaces.validator

Source Code of org.richfaces.validator.BeanValidator$ValidationResolver

/**
*
*/
package org.richfaces.validator;

import java.beans.FeatureDescriptor;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;

/**
* Perform validation by Hibernate Validator annotations
*
* @author asmirnov
*
*/
public class BeanValidator {

  private static final String RESOURCE_BUNDLE_IS_NOT_REGISTERED_FOR_CURRENT_LOCALE = "Resource bundle is not registered for current locale";

  private static final String FACES_CONTEXT_IS_NULL = "Faces context is null";

  private static final String INPUT_PARAMETERS_IS_NOT_CORRECT = "Input parameters is not correct.";

  private static final String LOCALE_IS_NOT_SET = "Locale is not set";

  private static final String VIEW_ROOT_IS_NOT_INITIALIZED = "ViewRoot is not initialized";

  public static final String VALIDATOR_PARAM = BeanValidator.class.getName();

  private Map<ValidatorKey, ClassValidator<? extends Object>> classValidators = new ConcurrentHashMap<ValidatorKey, ClassValidator<? extends Object>>();

  private BeanValidator() {
    // This is a "singleton"-like class. Only factory methods allowed.
  }

  /**
   * Create BeanValidator instance. For a Junit tests only.
   *
   * @return
   */
  static BeanValidator createInstance() {
    // TODO - get instance class name from a "META-INF/service"
    // If the Seam framework is active, use org.jboss.seam.core.Validators
    // component should be used.
    return new BeanValidator();
  }

  private static final Object MUTEX = new Object();

  /**
   * Return BeanValidator object from a ServletContext attribute. Create new
   * instance if none is defined.
   *
   * @param context
   * @return
   */
  public static BeanValidator getInstance(FacesContext context) {
    ExternalContext externalContext = context.getExternalContext();
    externalContext.getContext();
    BeanValidator instance;
    // TODO - use properly synchronization mutex ?
    synchronized (MUTEX) {
      Map<String, Object> applicationMap = externalContext
          .getApplicationMap();
      instance = (BeanValidator) applicationMap.get(VALIDATOR_PARAM);
      if (null == instance) {
        // Vaildator not initialized - create and store new instance.
        instance = createInstance();
        applicationMap.put(VALIDATOR_PARAM, instance);
      }
    }
    return instance;
  }

  /**
   * Perform Validation for a new value.
   *
   * @param context
   *            current faces context.
   * @param target
   *            {@link ValueExpression} for a value assignment.
   * @param value
   *            new value for validation
   * @return null if no validation errors. Array of the validation messages
   *         otherwise.
   * @throws FacesException
   *             if locale or context not properly initialized
   */
  public String[] validate(FacesContext context, ValueExpression target,
      Object value) {
    // TODO - check null parameters.
    if (null == context) {
      throw new FacesException(INPUT_PARAMETERS_IS_NOT_CORRECT);
    }
    String[] validationMessages = null;
    if (null != target) {
      ELContext elContext = context.getELContext();
      ValidationResolver validationResolver = new ValidationResolver(
          elContext.getELResolver(), calculateLocale(context));
      ELContextWrapper wrappedElContext = new ELContextWrapper(elContext,
          validationResolver);
      // TODO - handle ELExceptions ?
      try {
        target.setValue(wrappedElContext, value);
      } catch (ELException e) {
        throw new FacesException(e);
      }
      if (!validationResolver.isValid()) {
        validationMessages = validationResolver.getValidationMessages();
      }

    }
    return validationMessages;
  }

  protected Locale calculateLocale(FacesContext context) {
    if (null == context.getViewRoot()) {
      throw new FacesException(VIEW_ROOT_IS_NOT_INITIALIZED);
    } else if (null == context.getViewRoot().getLocale()) {
      throw new FacesException(LOCALE_IS_NOT_SET);
    }
    Locale locale = context.getViewRoot().getLocale();
    return locale;
  }

  // Method for checking input parameters for prevent NPE
  private void checkInputParameters(FacesContext context,
      ValueExpression target) {
    if (null == context || null == target ) {
      throw new FacesException(INPUT_PARAMETERS_IS_NOT_CORRECT);
    }
  }

  /**
   * Validate bean property for a new value. TODO - localization ?
   *
   * @param base
   *            - bean
   * @param property
   *            - bean property name.
   * @param value
   *            new value.
   * @return null for a valid value, array of the validation messages
   *         othervise.
   */
  public String[] validate(Object base, String property, Object value,
      Locale locale) {
    InvalidValue[] invalidValues = validateBean(base, property, value,
        locale);
    if (null == invalidValues) {
      return null;
    } else {
      String[] result = new String[invalidValues.length];
      for (int i = 0; i < invalidValues.length; i++) {
        InvalidValue invalidValue = invalidValues[i];
        result[i] = invalidValue.getMessage();
      }
      return result;
    }
  }

  @SuppressWarnings("unchecked")
  public String[] validateGraph(FacesContext context, Object value,
      Set<String> profiles) {
    if (null == context) {
      throw new FacesException(INPUT_PARAMETERS_IS_NOT_CORRECT);
    }
    String validationMessages[] = null;
    if (null != value) {
      ClassValidator<Object> validator = (ClassValidator<Object>) getValidator(
          value.getClass(), calculateLocale(context));
      if (validator.hasValidationRules()) {
        InvalidValue[] invalidValues = validator
            .getInvalidValues(value);
        if (null != invalidValues && invalidValues.length > 0) {
          validationMessages = new String[invalidValues.length];
          for (int i = 0; i < invalidValues.length; i++) {
            InvalidValue invalidValue = invalidValues[i];
            validationMessages[i] = invalidValue.getMessage();
          }
        }
      }

    }
    return validationMessages;
  }

  /**
   * Validate bean property of the base object aganist new value
   *
   * @param base
   * @param property
   * @param value
   * @return
   */
  protected InvalidValue[] validateBean(Object base, String property,
      Object value, Locale locale) {
    Class<? extends Object> beanClass = base.getClass();
    InvalidValue[] invalidValues = validateClass(beanClass, property,
        value, locale);
    return invalidValues;
  }

  /**
   * Validate bean property in the base class aganist new value.
   *
   * @param beanClass
   * @param property
   * @param value
   * @return
   */
  protected InvalidValue[] validateClass(Class<? extends Object> beanClass,
      String property, Object value, Locale locale) {
    ClassValidator<? extends Object> classValidator = getValidator(
        beanClass, locale);
    InvalidValue[] invalidValues = classValidator
        .getPotentialInvalidValues(property, value);
    return invalidValues;
  }

  /**
   * Get ( or create ) {@link ClassValidator} for a given bean class.
   *
   * @param beanClass
   * @return
   */
  @SuppressWarnings("unchecked")
  protected ClassValidator<? extends Object> getValidator(
      Class<? extends Object> beanClass, Locale locale) {
    // TODO - localization support.
    ValidatorKey key = new ValidatorKey(beanClass, locale);
    ClassValidator result = classValidators.get(key);
    if (null == result) {
      result = createValidator(beanClass, locale);
      classValidators.put(key, result);
    }
    return result;
  }

  /*
   * This method determine ResourceBundle, used in current request @param
   * locale - user locale @return ResourceBundle instance
   */
  private ResourceBundle getCurrentResourceBundle(Locale locale) {
    if (null == FacesContext.getCurrentInstance()
        || null == FacesContext.getCurrentInstance().getApplication()) {
      throw new FacesException(FACES_CONTEXT_IS_NULL);
    }
    String appBundle = FacesContext.getCurrentInstance().getApplication()
        .getMessageBundle();
    if (null == appBundle || null == locale) {
      return null;
    }
    ResourceBundle bundle = ResourceBundle.getBundle(appBundle, locale);
    return bundle;
  }

  /*
   * Method for create new instance of ClassValidator, if same not in cache.
   *
   * @param beanClass - Class to validate @param locale - user Locale, used
   * during validation process @return ClassValidator instance
   */
  @SuppressWarnings("unchecked")
  private ClassValidator<? extends Object> createValidator(
      Class<? extends Object> beanClass, Locale locale) {
    ResourceBundle bundle = getCurrentResourceBundle(locale);
    return bundle == null ? new ClassValidator(beanClass)
        : new ClassValidator(beanClass, bundle);
  }

  /**
   * Wrapper class for a {@link ELResolver}. For a setValue method, perform
   * validation instead of real assignment.
   *
   * @author asmirnov
   *
   */
  final class ValidationResolver extends ELResolver {

    /**
     * Original resolver.
     */
    private final ELResolver parent;

    private boolean valid = true;

    private String[] validationMessages = null;

    private Locale locale = null;
   
    /**
     * @param parent
     */
    public ValidationResolver(ELResolver parent, Locale locale) {
      this.parent = parent;
      this.locale = locale;
    }

    public boolean isValid() {
      // TODO Auto-generated method stub
      return valid;
    }

    /**
     * @param context
     * @param base
     * @return
     * @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext,
     *      java.lang.Object)
     */
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
      return parent.getCommonPropertyType(context, base);
    }

    /**
     * @param context
     * @param base
     * @return
     * @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext,
     *      java.lang.Object)
     */
    public Iterator<FeatureDescriptor> getFeatureDescriptors(
        ELContext context, Object base) {
      return parent.getFeatureDescriptors(context, base);
    }

    /**
     * @param context
     * @param base
     * @param property
     * @return
     * @see javax.el.ELResolver#getType(javax.el.ELContext,
     *      java.lang.Object, java.lang.Object)
     */
    public Class<?> getType(ELContext context, Object base, Object property) {
      return parent.getType(context, base, property);
    }

    /**
     * @param context
     * @param base
     * @param property
     * @return
     * @see javax.el.ELResolver#getValue(javax.el.ELContext,
     *      java.lang.Object, java.lang.Object)
     */
    public Object getValue(ELContext context, Object base, Object property) {
      return parent.getValue(context, base, property);
    }

    /**
     * @param context
     * @param base
     * @param property
     * @return
     * @see javax.el.ELResolver#isReadOnly(javax.el.ELContext,
     *      java.lang.Object, java.lang.Object)
     */
    public boolean isReadOnly(ELContext context, Object base,
        Object property) {
      return parent.isReadOnly(context, base, property);
    }

    /**
     * @param context
     * @param base
     * @param property
     * @param value
     * @see javax.el.ELResolver#setValue(javax.el.ELContext,
     *      java.lang.Object, java.lang.Object, java.lang.Object)
     */
    public void setValue(ELContext context, Object base, Object property,
        Object value) {
      if (null != base && null != property) {
        context.setPropertyResolved(true);
        //https://jira.jboss.org/jira/browse/RF-4034
        //apache el looses locale information during value resolution,
        //so we use our own
        validationMessages = validate(base, property.toString(), value, locale);
        valid = null == validationMessages
            || 0 == validationMessages.length;
      }
    }

    /**
     * @return the validationMessages
     */
    public String[] getValidationMessages() {
      return validationMessages;
    }

  }

  /**
   * Class for identify validator instance by locale
   *
   * @author amarkhel
   *
   */
  static class ValidatorKey {
    private final Class<? extends Object> validatableClass;
    private final Locale locale;

    /**
     * Constructor for ValidatorKey object
     *
     * @param validatableClass
     *            - class to validate
     * @param locale
     *            - User locale to determine Resource bundle, used during
     *            validation process
     */
    public ValidatorKey(Class<? extends Object> validatableClass,
        Locale locale) {
      this.validatableClass = validatableClass;
      this.locale = locale;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result
          + ((locale == null) ? 0 : locale.hashCode());
      result = prime
          * result
          + ((validatableClass == null) ? 0 : validatableClass
              .hashCode());
      return result;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof ValidatorKey))
        return false;
      ValidatorKey other = (ValidatorKey) obj;
      if (locale == null) {
        if (other.locale != null)
          return false;
      } else if (!locale.equals(other.locale))
        return false;
      if (validatableClass == null) {
        if (other.validatableClass != null)
          return false;
      } else if (!validatableClass.equals(other.validatableClass))
        return false;
      return true;
    }

  }
}
TOP

Related Classes of org.richfaces.validator.BeanValidator$ValidationResolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.