/* * Copyright 2010 Google Inc. * * 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 com.google.gwt.validation.client.impl; import com.google.gwt.validation.client.impl.metadata.BeanMetadata; import com.google.gwt.validation.client.impl.metadata.MessageAndPath; import com.google.gwt.validation.client.impl.metadata.ValidationGroupsMetadata; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.validation.ConstraintValidator; import javax.validation.ConstraintViolation; import javax.validation.MessageInterpolator; import javax.validation.ValidationException; import javax.validation.groups.Default; /** * Base methods for implementing a {@link GwtSpecificValidator}. * <p> * All methods that do not need to be generated go here. * * @param <G> the type object to validate */ public abstract class AbstractGwtSpecificValidator<G> implements GwtSpecificValidator<G> { /** * Builds attributes one at a time. * <p> * Used to create a attribute map for annotations. */ public static final class AttributeBuilder { private final HashMap<String, Object> tempMap = new HashMap<String, Object>(); private AttributeBuilder() { } public Map<String, Object> build() { return Collections.unmodifiableMap(tempMap); } public AttributeBuilder put(String key, Object value) { tempMap.put(key, value); return this; } } public static AttributeBuilder attributeBuilder() { return new AttributeBuilder(); } protected static Class<?>[] groupsToClasses(Group... groups) { int numGroups = groups.length; Class<?>[] array = new Class<?>[numGroups]; for (int i = 0; i < numGroups; i++) { array[i] = groups[i].getGroup(); } return array; } @Override public <T> Set<ConstraintViolation<T>> validate( GwtValidationContext<T> context, G object, Class<?>... groups) { context.addValidatedObject(object); try { GroupValidator classGroupValidator = new ClassGroupValidator(object); GroupChain groupChain = createGroupChainFromGroups(context, groups); BeanMetadata beanMetadata = getBeanMetadata(); List<Class<?>> defaultGroupSeq = beanMetadata.getDefaultGroupSequence(); if (beanMetadata.defaultGroupSequenceIsRedefined()) { // only need to check this on class-level validation groupChain.checkDefaultGroupSequenceIsExpandable(defaultGroupSeq); } return validateGroups(context, classGroupValidator, groupChain); } catch (IllegalArgumentException e) { throw e; } catch (ValidationException e) { throw e; } catch (Exception e) { throw new ValidationException("Error validating " + object.getClass(), e); } } @Override public <T> Set<ConstraintViolation<T>> validateProperty( GwtValidationContext<T> context, G object, String propertyName, Class<?>... groups) throws ValidationException { try { GroupValidator propertyGroupValidator = new PropertyGroupValidator(object, propertyName); GroupChain groupChain = createGroupChainFromGroups(context, groups); return validateGroups(context, propertyGroupValidator, groupChain); } catch (IllegalArgumentException e) { throw e; } catch (ValidationException e) { throw e; } catch (Exception e) { throw new ValidationException("Error validating property " + propertyName + " of " + object.getClass(), e); } } @Override public <T> Set<ConstraintViolation<T>> validateValue( GwtValidationContext<T> context, Class<G> beanType, String propertyName, Object value, Class<?>... groups) throws ValidationException { try { GroupValidator valueGroupValidator = new ValueGroupValidator(beanType, propertyName, value); GroupChain groupChain = createGroupChainFromGroups(context, groups); return validateGroups(context, valueGroupValidator, groupChain); } catch (IllegalArgumentException e) { throw e; } catch (ValidationException e) { throw e; } catch (Exception e) { throw new ValidationException("Error validating property " + propertyName + " with value " + value + " of " + beanType, e); } } protected List<Class<?>> addDefaultGroupWhenEmpty(List<Class<?>> groups) { if (groups.isEmpty()) { groups = new ArrayList<Class<?>>(); groups.add(Default.class); } return groups; } protected <V, T, A extends Annotation> void addSingleViolation( GwtValidationContext<T> context, Set<ConstraintViolation<T>> violations, G object, V value, ConstraintDescriptorImpl<A> constraintDescriptor) { ConstraintValidatorContextImpl<A, V> constraintValidatorContext = context.createConstraintValidatorContext(constraintDescriptor); addViolations(context, violations, object, value, constraintDescriptor, constraintValidatorContext); } /** * Perform the actual validation of a single {@link ConstraintValidator}. * <p> * As a side effect {@link ConstraintViolation}s may be added to * {@code violations}. * * @return true if there was any constraint violations */ protected <A extends Annotation, T, V> boolean validate( GwtValidationContext<T> context, Set<ConstraintViolation<T>> violations, G object, V value, ConstraintValidator<A, ? super V> validator, ConstraintDescriptorImpl<A> constraintDescriptor, Class<?>... groups) { validator.initialize(constraintDescriptor.getAnnotation()); ConstraintValidatorContextImpl<A, V> constraintValidatorContext = context.createConstraintValidatorContext(constraintDescriptor); List<Class<?>> groupsList = Arrays.asList(groups); ValidationGroupsMetadata validationGroupsMetadata = context.getValidator().getValidationGroupsMetadata(); Set<Class<?>> constraintGroups = constraintDescriptor.getGroups(); // check groups requested are in the set of constraint groups (including the implicit group) if (!containsAny(groupsList, constraintGroups) && !groupsList.contains(getConstraints(validationGroupsMetadata).getElementClass())) { return false; } if (!validator.isValid(value, constraintValidatorContext)) { addViolations(// context, // violations, // object, // value, // constraintDescriptor, // constraintValidatorContext); return true; } return false; } private <V, T, A extends Annotation> void addViolations( GwtValidationContext<T> context, Set<ConstraintViolation<T>> violations, G object, V value, ConstraintDescriptorImpl<A> constraintDescriptor, ConstraintValidatorContextImpl<A, V> constraintValidatorContext) { Set<MessageAndPath> mps = constraintValidatorContext.getMessageAndPaths(); for (MessageAndPath messageAndPath : mps) { ConstraintViolation<T> violation = createConstraintViolation(// context, // object, // value, // constraintDescriptor, // messageAndPath); violations.add(violation); } } private <T> boolean containsAny(Collection<T> left, Collection<T> right) { for (T t : left) { if (right.contains(t)) { return true; } } return false; } private <T, V, A extends Annotation> ConstraintViolation<T> createConstraintViolation( GwtValidationContext<T> context, G object, V value, ConstraintDescriptorImpl<A> constraintDescriptor, MessageAndPath messageAndPath) { MessageInterpolator messageInterpolator = context.getMessageInterpolator(); com.google.gwt.validation.client.impl.MessageInterpolatorContextImpl messageContext = new MessageInterpolatorContextImpl( constraintDescriptor, value); String message = messageInterpolator.interpolate( messageAndPath.getMessage(), messageContext); ConstraintViolation<T> violation = ConstraintViolationImpl.<T> builder() // .setConstraintDescriptor(constraintDescriptor) // .setInvalidValue(value) // .setLeafBean(object) // .setMessage(message) // .setMessageTemplate(messageAndPath.getMessage()) // .setPropertyPath(messageAndPath.getPath()) // .setRootBean(context.getRootBean()) // .setRootBeanClass(context.getRootBeanClass()) // .setElementType(constraintDescriptor.getElementType()) // .build(); return violation; } private <T> GroupChain createGroupChainFromGroups(GwtValidationContext<T> context, Class<?>... groups) { List<Class<?>> groupsList = addDefaultGroupWhenEmpty(Arrays.asList(groups)); ValidationGroupsMetadata validationGroupsMetadata = context.getValidator().getValidationGroupsMetadata(); return new GroupChainGenerator(validationGroupsMetadata).getGroupChainFor(groupsList); } /** * Performs the top-level validation using a helper {@link GroupValidator}. This takes * group sequencing and Default group overriding into account. */ private <T> Set<ConstraintViolation<T>> validateGroups( GwtValidationContext<T> context, GroupValidator groupValidator, GroupChain groupChain) { Set<ConstraintViolation<T>> violations = new HashSet<ConstraintViolation<T>>(); Collection<Group> allGroups = groupChain.getAllGroups(); Group[] allGroupsArray = allGroups.toArray(new Group[allGroups.size()]); groupValidator.validateGroups(context, violations, allGroupsArray); // handle sequences Iterator<List<Group>> sequenceIterator = groupChain.getSequenceIterator(); while (sequenceIterator.hasNext()) { List<Group> sequence = sequenceIterator.next(); for (Group group : sequence) { int numberOfViolations = violations.size(); groupValidator.validateGroups(context, violations, group); if (violations.size() > numberOfViolations) { // stop processing when an error occurs break; } } } return violations; } private class ClassGroupValidator implements GroupValidator { private final G object; public ClassGroupValidator(G object) { this.object = object; } @Override public <T> void validateGroups(GwtValidationContext<T> context, Set<ConstraintViolation<T>> violations, Group... groups) { expandDefaultAndValidateClassGroups(context, object, violations, groups); } } private class PropertyGroupValidator implements GroupValidator { private final G object; private final String propertyName; public PropertyGroupValidator(G object, String propertyName) { this.object = object; this.propertyName = propertyName; } @Override public <T> void validateGroups(GwtValidationContext<T> context, Set<ConstraintViolation<T>> violations, Group... groups) { expandDefaultAndValidatePropertyGroups(context, object, propertyName, violations, groups); } } private class ValueGroupValidator implements GroupValidator { private final Class<G> beanType; private final String propertyName; private final Object value; public ValueGroupValidator(Class<G> beanType, String propertyName, Object value) { this.beanType = beanType; this.propertyName = propertyName; this.value = value; } @Override public <T> void validateGroups(GwtValidationContext<T> context, Set<ConstraintViolation<T>> violations, Group... groups) { expandDefaultAndValidateValueGroups(context, beanType, propertyName, value, violations, // groups); } } }