Package org.apache.wicket.bean.validation

Source Code of org.apache.wicket.bean.validation.PropertyValidator

package org.apache.wicket.bean.validation;

import java.util.Iterator;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;
import javax.validation.metadata.ConstraintDescriptor;

import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;

/**
* Validator that delegates to the bean validation framework. The integration has to be first
* configured using {@link BeanValidationConfiguration}.
*
* <p>
* The validator must be provided a {@link Property}, unless one can be resolved from the component
* implicitly. By default the configuration contains the {@link DefaultPropertyResolver} so
* {@link PropertyModel}s are supported out of the box - when attached to a component with a
* property model the property does not need to be specified explicitly.
* </p>
*
* <p>
* The validator will set the required flag on the form component it is attached to based on the
* presence of the @NotNull annotation. Notice, the required flag will only be set to {@code true},
* components with the required flag already set to {@code true} will not have the flag set to
* {@code false} by this validator.
* </p>
*
* <p>
* The validator will allow {@link ITagModifier}s configured in {@link BeanValidationConfiguration}
* to mutate the markup tag of the component it is attached to.
* </p>
*
* <p>
* The validator specifies default error messages in the {@code PropertyValidator.properties} file.
* These values can be overridden in the application subclass' property files globally or in the
* page or panel properties locally. See this file for the default messages supported.
* </p>
*
* @author igor
*
* @param <T>
*/
public class PropertyValidator<T> extends Behavior implements IValidator<T>
{
  private static final Class<?>[] EMPTY = new Class<?>[0];

  private FormComponent<T> component;

  // the trailing underscore means that these members should not be used
  // directly. ALWAYS use the respective getter instead.
  private Property property_;
  private final IModel<Class<?>[]> groups_;

  public PropertyValidator(Class<?>... groups)
  {
    this(null, groups);
  }

  public PropertyValidator(IModel<Class<?>[]> groups)
  {
    this(null, groups);
  }

  public PropertyValidator(Property property, Class<?>... groups)
  {
    this(property, new GroupsModel(groups));
  }

  public PropertyValidator(Property property, IModel<Class<?>[]> groups)
  {
    this.property_ = property;
    this.groups_ = groups;
  }

  private Property getProperty()
  {
    if (property_ == null)
    {
      property_ = BeanValidationConfiguration.get().resolveProperty(component);
      if (property_ == null)
      {
        throw new IllegalStateException(
          "Could not resolve Property from component: " +
            component +
            ". Either specify the Property in the constructor or use a model that works in combination with a " +
            IPropertyResolver.class.getSimpleName() +
            " to resolve the Property automatically");
      }
    }
    return property_;
  }

  private Class<?>[] getGroups()
  {
    if (groups_ == null)
    {
      return EMPTY;
    }
    return groups_.getObject();
  }

  @SuppressWarnings("unchecked")
  @Override
  public void bind(Component component)
  {
    if (this.component != null)
    {
      throw new IllegalStateException( //
        "This validator has already been added to component: " + this.component +
          ". This validator does not support reusing instances, please create a new one");
    }

    if (!(component instanceof FormComponent))
    {
      throw new IllegalStateException(getClass().getSimpleName() +
        " can only be added to FormComponents");
    }

    // TODO add a validation key that appends the type so we can have different messages for
    // @Size on String vs Collection - done but need to add a key for each superclass/interface

    this.component = (FormComponent<T>)component;

    setComponentRequiredFlag();
  }


  @Override
  public void detach(Component component)
  {
    super.detach(component);
    if (groups_ != null)
    {
      groups_.detach();
    }
  }

  /**
   * Marks the form component required if necessary
   */
  private void setComponentRequiredFlag()
  {
    BeanValidationContext config = BeanValidationConfiguration.get();
    Validator validator = config.getValidator();
    Property property = getProperty();

    // if the property has a NotNull constraint mark the form component required

    Iterator<ConstraintDescriptor<?>> it = new ConstraintIterator(validator, property);
    while (it.hasNext())
    {
      ConstraintDescriptor<?> desc = it.next();
      if (desc.getAnnotation().annotationType().equals(NotNull.class))
      {
        component.setRequired(true);
        break;
      }
    }
  }

  @Override
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public void onComponentTag(Component component, ComponentTag tag)
  {
    super.onComponentTag(component, tag);

    BeanValidationContext config = BeanValidationConfiguration.get();
    Validator validator = config.getValidator();
    Property property = getProperty();

    // find any tag modifiers that apply to the constraints of the property being validated
    // and allow them to modify the component tag

    Iterator<ConstraintDescriptor<?>> it = new ConstraintIterator(validator, property,
      getGroups());

    while (it.hasNext())
    {
      ConstraintDescriptor<?> desc = it.next();

      ITagModifier modifier = config.getTagModifier(desc.getAnnotation().annotationType());

      if (modifier != null)
      {
        modifier.modify((FormComponent<?>)component, tag, desc.getAnnotation());
      }
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public void validate(IValidatable<T> validatable)
  {
    BeanValidationContext config = BeanValidationConfiguration.get();
    Validator validator = config.getValidator();

    Property property = getProperty();

    // validate the value using the bean validator

    Set<?> violations = validator.validateValue(property.getOwner(), property.getName(),
      validatable.getValue(), getGroups());

    // iterate over violations and report them

    for (ConstraintViolation<?> violation : (Set<ConstraintViolation<?>>)violations)
    {
      validatable.error(config.getViolationTranslator().convert(violation));
    }
  }

}
TOP

Related Classes of org.apache.wicket.bean.validation.PropertyValidator

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.