| /* |
| * 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.rebind; |
| |
| import com.google.gwt.core.client.GWT; |
| import com.google.gwt.core.client.UnsafeNativeLong; |
| import com.google.gwt.core.ext.Generator; |
| import com.google.gwt.core.ext.GeneratorContext; |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.core.ext.typeinfo.JClassType; |
| import com.google.gwt.core.ext.typeinfo.JField; |
| import com.google.gwt.core.ext.typeinfo.JMethod; |
| import com.google.gwt.core.ext.typeinfo.JPrimitiveType; |
| import com.google.gwt.core.ext.typeinfo.JType; |
| import com.google.gwt.dev.jjs.ast.JBooleanLiteral; |
| import com.google.gwt.dev.jjs.ast.JCharLiteral; |
| import com.google.gwt.dev.jjs.ast.JDoubleLiteral; |
| import com.google.gwt.dev.jjs.ast.JFloatLiteral; |
| import com.google.gwt.dev.jjs.ast.JIntLiteral; |
| import com.google.gwt.dev.jjs.ast.JLongLiteral; |
| import com.google.gwt.thirdparty.guava.common.base.Function; |
| import com.google.gwt.thirdparty.guava.common.base.Functions; |
| import com.google.gwt.thirdparty.guava.common.base.Joiner; |
| import com.google.gwt.thirdparty.guava.common.base.Predicate; |
| import com.google.gwt.thirdparty.guava.common.collect.ImmutableList; |
| import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet; |
| import com.google.gwt.thirdparty.guava.common.collect.Iterables; |
| import com.google.gwt.thirdparty.guava.common.collect.Maps; |
| import com.google.gwt.thirdparty.guava.common.collect.Ordering; |
| import com.google.gwt.thirdparty.guava.common.collect.Sets; |
| import com.google.gwt.thirdparty.guava.common.primitives.Primitives; |
| import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; |
| import com.google.gwt.user.rebind.SourceWriter; |
| import com.google.gwt.validation.client.impl.AbstractGwtSpecificValidator; |
| import com.google.gwt.validation.client.impl.ConstraintDescriptorImpl; |
| import com.google.gwt.validation.client.impl.ConstraintOrigin; |
| import com.google.gwt.validation.client.impl.Group; |
| import com.google.gwt.validation.client.impl.GroupChain; |
| import com.google.gwt.validation.client.impl.GroupChainGenerator; |
| import com.google.gwt.validation.client.impl.GwtBeanDescriptor; |
| import com.google.gwt.validation.client.impl.GwtBeanDescriptorImpl; |
| import com.google.gwt.validation.client.impl.GwtValidationContext; |
| import com.google.gwt.validation.client.impl.PathImpl; |
| import com.google.gwt.validation.client.impl.PropertyDescriptorImpl; |
| import com.google.gwt.validation.client.impl.metadata.BeanMetadata; |
| import com.google.gwt.validation.client.impl.metadata.ValidationGroupsMetadata; |
| |
| import java.beans.BeanInfo; |
| import java.beans.IntrospectionException; |
| import java.beans.Introspector; |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.ElementType; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import javax.validation.Constraint; |
| import javax.validation.ConstraintValidator; |
| import javax.validation.ConstraintViolation; |
| import javax.validation.GroupSequence; |
| import javax.validation.Path.Node; |
| import javax.validation.Payload; |
| import javax.validation.UnexpectedTypeException; |
| import javax.validation.Valid; |
| import javax.validation.ValidationException; |
| import javax.validation.groups.Default; |
| import javax.validation.metadata.BeanDescriptor; |
| import javax.validation.metadata.ConstraintDescriptor; |
| import javax.validation.metadata.PropertyDescriptor; |
| |
| /** |
| * Creates a {@link com.google.gwt.validation.client.impl.GwtSpecificValidator}. |
| * <p> |
| * This class is not thread safe. |
| */ |
| public final class GwtSpecificValidatorCreator extends AbstractCreator { |
| private static enum Stage { |
| OBJECT, PROPERTY, VALUE |
| } |
| |
| static final JType[] NO_ARGS = new JType[]{}; |
| |
| private static final String DEFAULT_VIOLATION_VAR = "violations"; |
| |
| private static final Annotation[] NO_ANNOTATIONS = new Annotation[]{}; |
| |
| private static Function<java.beans.PropertyDescriptor, String> |
| PROPERTY_DESCRIPTOR_TO_NAME = |
| new Function<java.beans.PropertyDescriptor, String>() { |
| @Override |
| public String apply(java.beans.PropertyDescriptor pd) { |
| return pd.getName(); |
| } |
| }; |
| |
| private static Function<Object, String> TO_LITERAL = new Function<Object, String>() { |
| |
| @Override |
| public String apply(Object input) { |
| return asLiteral(input); |
| } |
| }; |
| |
| public static String asGetter(PropertyDescriptor p) { |
| return "get" + capitalizeFirstLetter(p.getPropertyName()); |
| } |
| |
| /** |
| * Returns the literal value of an object that is suitable for inclusion in |
| * Java Source code. |
| * |
| * <p> |
| * Supports all types that {@link Annotation} value can have. |
| * |
| * |
| * @throws IllegalArgumentException if the type of the object does not have a java literal form. |
| */ |
| public static String asLiteral(Object value) throws IllegalArgumentException { |
| Class<?> clazz = value.getClass(); |
| |
| if (clazz.isArray()) { |
| StringBuilder sb = new StringBuilder(); |
| Object[] array = (Object[]) value; |
| |
| sb.append("new " + clazz.getComponentType().getCanonicalName() + "[] "); |
| sb.append("{"); |
| boolean first = true; |
| for (Object object : array) { |
| if (first) { |
| first = false; |
| } else { |
| sb.append(","); |
| } |
| sb.append(asLiteral(object)); |
| } |
| sb.append("}"); |
| return sb.toString(); |
| } |
| |
| if (value instanceof Boolean) { |
| return JBooleanLiteral.get(((Boolean) value).booleanValue()) |
| .toSource(); |
| } else if (value instanceof Byte) { |
| return JIntLiteral.get(((Byte) value).byteValue()).toSource(); |
| } else if (value instanceof Character) { |
| return JCharLiteral.get(((Character) value).charValue()) |
| .toSource(); |
| } else if (value instanceof Class<?>) { |
| return ((Class<?>) ((Class<?>) value)).getCanonicalName() + ".class"; |
| } else if (value instanceof Double) { |
| return JDoubleLiteral.get(((Double) value).doubleValue()) |
| .toSource(); |
| } else if (value instanceof Enum) { |
| return value.getClass().getCanonicalName() + "." |
| + ((Enum<?>) value).name(); |
| } else if (value instanceof Float) { |
| return JFloatLiteral.get(((Float) value).floatValue()).toSource(); |
| } else if (value instanceof Integer) { |
| return JIntLiteral.get(((Integer) value).intValue()).toSource(); |
| } else if (value instanceof Long) { |
| return JLongLiteral.get(((Long) value).longValue()).toSource(); |
| } else if (value instanceof String) { |
| return '"' + Generator.escape((String) value) + '"'; |
| } else { |
| // TODO(nchalko) handle Annotation types |
| throw new IllegalArgumentException(value.getClass() |
| + " can not be represented as a Java Literal."); |
| } |
| } |
| |
| public static String capitalizeFirstLetter(String propertyName) { |
| if (propertyName == null) { |
| return null; |
| } |
| if (propertyName.length() == 0) { |
| return ""; |
| } |
| String cap = propertyName.substring(0, 1).toUpperCase(Locale.ROOT); |
| if (propertyName.length() > 1) { |
| cap += propertyName.substring(1); |
| } |
| return cap; |
| } |
| |
| public static boolean isIterableOrMap(Class<?> elementClass) { |
| // TODO(nchalko) handle iterables everywhere this is called. |
| return elementClass.isArray() |
| || Iterable.class.isAssignableFrom(elementClass) |
| || Map.class.isAssignableFrom(elementClass); |
| } |
| |
| /** |
| * Finds the type that a constraint validator will check. |
| * |
| * <p>This type comes from the first parameter of the isValid() method on |
| * the constraint validator. However, this is a bit tricky because ConstraintValidator |
| * has a parameterized type. When using Java reflection, we will see multiple isValid() |
| * methods, including one that checks java.lang.Object.</p> |
| * |
| * <p>Strategy: for now, assume there are at most two isValid() methods. If there are two, |
| * assume one of them has a type that is assignable from the other. (Most likely, |
| * one of them will be java.lang.Object.)</p> |
| * |
| * @throws IllegalStateException if there isn't any isValid() method or there are more than two. |
| */ |
| static <T extends Annotation> Class<?> getTypeOfConstraintValidator( |
| Class<? extends ConstraintValidator<T, ?>> constraintClass) { |
| |
| int candidateCount = 0; |
| Class<?> result = null; |
| for (Method method : constraintClass.getMethods()) { |
| if (method.getName().equals("isValid") |
| && method.getParameterTypes().length == 2 |
| && method.getReturnType().isAssignableFrom(Boolean.TYPE)) { |
| Class<?> firstArgType = method.getParameterTypes()[0]; |
| if (result == null || result.isAssignableFrom(firstArgType)) { |
| result = firstArgType; |
| } |
| candidateCount++; |
| } |
| } |
| |
| if (candidateCount == 0) { |
| throw new IllegalStateException("ConstraintValidators must have a isValid method"); |
| } else if (candidateCount > 2) { |
| throw new IllegalStateException( |
| "ConstraintValidators must have no more than two isValid methods"); |
| } |
| |
| return result; |
| } |
| |
| // Visible for testing |
| static <A extends Annotation> ImmutableSet<Class<? extends ConstraintValidator<A, ?>>> getValidatorForType( |
| Class<?> type, |
| List<Class<? extends ConstraintValidator<A, ?>>> constraintValidatorClasses) { |
| type = Primitives.wrap(type); |
| Map<Class<?>, Class<? extends ConstraintValidator<A, ?>>> map = Maps.newHashMap(); |
| for (Class<? extends ConstraintValidator<A, ?>> constraintClass : constraintValidatorClasses) { |
| Class<?> aType = Primitives.wrap(getTypeOfConstraintValidator(constraintClass)); |
| if (aType.isAssignableFrom(type)) { |
| map.put(aType, constraintClass); |
| } |
| } |
| // TODO(nchalko) implement per spec |
| // Handle Arrays and Generics |
| |
| final Set<Class<?>> best = Util.findBestMatches(type, map.keySet()); |
| |
| Predicate<Class<?>> inBest = new Predicate<Class<?>>() { |
| |
| @Override |
| public boolean apply(Class<?> key) { |
| return best.contains(key); |
| } |
| }; |
| return ImmutableSet.copyOf(Maps.filterKeys(map, inBest).values()); |
| } |
| |
| private static ConstraintOrigin convertConstraintOriginEnum( |
| org.hibernate.validator.metadata.ConstraintOrigin definedOn) { |
| switch (definedOn) { |
| case DEFINED_IN_HIERARCHY: |
| return ConstraintOrigin.DEFINED_IN_HIERARCHY; |
| case DEFINED_LOCALLY: |
| return ConstraintOrigin.DEFINED_LOCALLY; |
| default: |
| throw new IllegalArgumentException("Unable to convert: unknown ConstraintOrigin value"); |
| } |
| } |
| |
| /** |
| * Gets the best {@link ConstraintValidator}. |
| * |
| * <p> |
| * The ConstraintValidator chosen to validate a declared type |
| * {@code targetType} is the one where the type supported by the |
| * ConstraintValidator is a supertype of {@code targetType} and where there is |
| * no other ConstraintValidator whose supported type is a supertype of |
| * {@code type} and not a supertype of the chosen ConstraintValidator |
| * supported type. |
| * |
| * @param constraint the constraint to find ConstraintValidators for. |
| * @param targetType The type to find a ConstraintValidator for. |
| * @return ConstraintValidator |
| * |
| * @throws UnexpectedTypeException if there is not exactly one maximally |
| * specific constraint validator for targetType. |
| */ |
| private static <A extends Annotation> Class<? extends ConstraintValidator<A, ?>> getValidatorForType( |
| ConstraintDescriptor<A> constraint, Class<?> targetType) |
| throws UnexpectedTypeException { |
| List<Class<? extends ConstraintValidator<A, ?>>> constraintValidatorClasses |
| = constraint.getConstraintValidatorClasses(); |
| if (constraintValidatorClasses.isEmpty()) { |
| throw new UnexpectedTypeException("No ConstraintValidator found for " |
| + constraint.getAnnotation()); |
| } |
| ImmutableSet<Class<? extends ConstraintValidator<A, ?>>> best = getValidatorForType( |
| targetType, constraintValidatorClasses); |
| if (best.isEmpty()) { |
| throw new UnexpectedTypeException("No " + constraint.getAnnotation() |
| + " ConstraintValidator for type " + targetType); |
| } |
| if (best.size() > 1) { |
| throw new UnexpectedTypeException("More than one maximally specific " |
| + constraint.getAnnotation() + " ConstraintValidator for type " |
| + targetType + ", found " + Ordering.usingToString().sortedCopy(best)); |
| } |
| return Iterables.get(best, 0); |
| } |
| |
| private final BeanHelper beanHelper; |
| |
| private final Set<BeanHelper> beansToValidate = Sets.newHashSet(); |
| |
| private final JClassType beanType; |
| |
| private final Set<JField> fieldsToWrap = Sets.newHashSet(); |
| |
| private Set<JMethod> gettersToWrap = Sets.newHashSet(); |
| |
| private final Set<Class<?>> validGroups; |
| |
| private final Map<ConstraintDescriptor<?>, Boolean> validConstraintsMap = Maps.newHashMap(); |
| |
| public GwtSpecificValidatorCreator(JClassType validatorType, |
| JClassType beanType, BeanHelper beanHelper, TreeLogger logger, |
| GeneratorContext context, BeanHelperCache cache, Class<?>[] validGroupsFromAnnotation) { |
| super(context, logger, validatorType, cache); |
| this.beanType = beanType; |
| this.beanHelper = beanHelper; |
| |
| Set<Class<?>> tempValidGroups = Sets.newHashSet(validGroupsFromAnnotation); |
| tempValidGroups.add(Default.class); |
| this.validGroups = Collections.unmodifiableSet(tempValidGroups); |
| } |
| |
| @Override |
| protected void compose(ClassSourceFileComposerFactory composerFactory) { |
| addImports(composerFactory, Annotation.class, ConstraintViolation.class, |
| GWT.class, ValidationGroupsMetadata.class, Group.class, GroupChain.class, PathImpl.class, |
| Node.class, GroupChainGenerator.class, GwtBeanDescriptor.class, BeanMetadata.class, |
| GwtValidationContext.class, ArrayList.class, HashSet.class, IllegalArgumentException.class, |
| Set.class, Collection.class, Iterator.class, List.class, ValidationException.class); |
| composerFactory.setSuperclass(AbstractGwtSpecificValidator.class.getCanonicalName() |
| + "<" + beanType.getQualifiedSourceName() + ">"); |
| composerFactory.addImplementedInterface(validatorType.getName()); |
| } |
| |
| @Override |
| protected void writeClassBody(SourceWriter sw) |
| throws UnableToCompleteException { |
| writeFields(sw); |
| sw.println(); |
| writeValidateClassGroups(sw); |
| sw.println(); |
| writeExpandDefaultAndValidateClassGroups(sw); |
| sw.println(); |
| writeExpandDefaultAndValidatePropertyGroups(sw); |
| sw.println(); |
| writeExpandDefaultAndValidateValueGroups(sw); |
| sw.println(); |
| writeValidatePropertyGroups(sw); |
| sw.println(); |
| writeValidateValueGroups(sw); |
| sw.println(); |
| writeGetBeanMetadata(sw); |
| sw.println(); |
| writeGetDescriptor(sw); |
| sw.println(); |
| writePropertyValidators(sw); |
| sw.println(); |
| writeValidateAllNonInheritedProperties(sw); |
| sw.println(); |
| |
| // Write the wrappers after we know which are needed |
| writeWrappers(sw); |
| sw.println(); |
| } |
| |
| protected void writeUnsafeNativeLongIfNeeded(SourceWriter sw, JType jType) { |
| if (JPrimitiveType.LONG.equals(jType)) { |
| // @com.google.gwt.core.client.UnsafeNativeLong |
| sw.print("@"); |
| sw.println(UnsafeNativeLong.class.getCanonicalName()); |
| } |
| } |
| |
| private boolean areConstraintDescriptorGroupsValid(ConstraintDescriptor<?> constraintDescriptor) { |
| if (validConstraintsMap.containsKey(constraintDescriptor)) { |
| return validConstraintsMap.get(constraintDescriptor); |
| } else { |
| boolean areValid = checkGroups(constraintDescriptor.getGroups()); |
| // cache result |
| validConstraintsMap.put(constraintDescriptor, areValid); |
| return areValid; |
| } |
| } |
| |
| private <T> T[] asArray(Collection<?> collection, T[] array) { |
| if (collection == null) { |
| return null; |
| } |
| return collection.toArray(array); |
| } |
| |
| private boolean checkGroups(Set<Class<?>> groups) { |
| for (Class<?> group : groups) { |
| if (validGroups.contains(group)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private String constraintDescriptorVar(String name, int count) { |
| String s = name + "_c" + count; |
| return s; |
| } |
| |
| private Annotation getAnnotation(PropertyDescriptor p, boolean useField, |
| Class<? extends Annotation> expectedAnnotationClass) { |
| Annotation annotation = null; |
| if (useField) { |
| JField field = beanType.findField(p.getPropertyName()); |
| if (field.getEnclosingType().equals(beanType)) { |
| annotation = field.getAnnotation(expectedAnnotationClass); |
| } |
| } else { |
| JMethod method = beanType.findMethod(asGetter(p), NO_ARGS); |
| if (method.getEnclosingType().equals(beanType)) { |
| annotation = method.getAnnotation(expectedAnnotationClass); |
| } |
| } |
| return annotation; |
| } |
| |
| private Annotation[] getAnnotations(PropertyDescriptor p, |
| boolean useField) { |
| Class<?> clazz = beanHelper.getClazz(); |
| if (useField) { |
| try { |
| Field field = clazz.getDeclaredField(p.getPropertyName()); |
| return field.getAnnotations(); |
| } catch (NoSuchFieldException ignore) { |
| // Expected Case |
| } |
| } else { |
| try { |
| Method method = clazz.getMethod(asGetter(p)); |
| return method.getAnnotations(); |
| } catch (NoSuchMethodException ignore) { |
| // Expected Case |
| } |
| } |
| return NO_ANNOTATIONS; |
| } |
| |
| private String getQualifiedSourceNonPrimitiveType(JType elementType) { |
| JPrimitiveType primitive = elementType.isPrimitive(); |
| return primitive == null ? elementType.getQualifiedSourceName() |
| : primitive.getQualifiedBoxedSourceName(); |
| } |
| |
| private boolean hasMatchingAnnotation(Annotation expectedAnnotation, |
| Annotation[] annotations) throws UnableToCompleteException { |
| // See spec section 2.2. Applying multiple constraints of the same type |
| for (Annotation annotation : annotations) { |
| // annotations not annotated by @Constraint |
| if (annotation.annotationType().getAnnotation(Constraint.class) == null) { |
| try { |
| // value element has a return type of an array of constraint |
| // annotations |
| Method valueMethod = annotation.annotationType().getMethod("value"); |
| Class<?> valueType = valueMethod.getReturnType(); |
| if (valueType.isArray() |
| && Annotation.class.isAssignableFrom(valueType.getComponentType())) { |
| if (Modifier.isAbstract(valueMethod.getModifiers())) { |
| // handle edge case where interface is marked "abstract" |
| valueMethod.setAccessible(true); |
| } |
| Annotation[] valueAnnotions = (Annotation[]) valueMethod.invoke(annotation); |
| for (Annotation annotation2 : valueAnnotions) { |
| if (expectedAnnotation.equals(annotation2)) { |
| return true; |
| } |
| } |
| } |
| } catch (NoSuchMethodException ignore) { |
| // Expected Case. |
| } catch (Exception e) { |
| throw error(logger, e); |
| } |
| } |
| } |
| return false; |
| } |
| |
| private boolean hasMatchingAnnotation(ConstraintDescriptor<?> constraint) |
| throws UnableToCompleteException { |
| Annotation expectedAnnotation = constraint.getAnnotation(); |
| Class<? extends Annotation> expectedAnnotationClass = expectedAnnotation.annotationType(); |
| if (expectedAnnotation.equals(beanHelper.getClazz().getAnnotation( |
| expectedAnnotationClass))) { |
| return true; |
| } |
| |
| // See spec section 2.2. Applying multiple constraints of the same type |
| Annotation[] annotations = beanHelper.getClazz().getAnnotations(); |
| return hasMatchingAnnotation(expectedAnnotation, annotations); |
| } |
| |
| private boolean hasMatchingAnnotation(PropertyDescriptor p, boolean useField, |
| ConstraintDescriptor<?> constraint) throws UnableToCompleteException { |
| Annotation expectedAnnotation = constraint.getAnnotation(); |
| Class<? extends Annotation> expectedAnnotationClass = |
| expectedAnnotation.annotationType(); |
| if (expectedAnnotation.equals(getAnnotation(p, useField, |
| expectedAnnotationClass))) { |
| return true; |
| } |
| return hasMatchingAnnotation(expectedAnnotation, |
| getAnnotations(p, useField)); |
| } |
| |
| private boolean hasValid(PropertyDescriptor p, boolean useField) { |
| return getAnnotation(p, useField, Valid.class) != null; |
| } |
| |
| private boolean isPropertyConstrained(BeanHelper helper, PropertyDescriptor p) { |
| Set<PropertyDescriptor> propertyDescriptors = |
| helper.getBeanDescriptor().getConstrainedProperties(); |
| Predicate<PropertyDescriptor> nameMatches = newPropertyNameMatches(p); |
| return Iterables.any(propertyDescriptors, nameMatches); |
| } |
| |
| private boolean isPropertyConstrained(PropertyDescriptor p, boolean useField) { |
| // cascaded counts as constrained |
| // we must know if the @Valid annotation is on a field or a getter |
| JClassType jClass = beanHelper.getJClass(); |
| if (useField && |
| jClass.findField(p.getPropertyName()).isAnnotationPresent(Valid.class)) { |
| return true; |
| } else if (!useField && |
| jClass.findMethod(asGetter(p),NO_ARGS).isAnnotationPresent(Valid.class)) { |
| return true; |
| } |
| // for non-cascaded properties |
| for (ConstraintDescriptor<?> constraint : p.getConstraintDescriptors()) { |
| org.hibernate.validator.metadata.ConstraintDescriptorImpl<?> constraintHibernate = |
| (org.hibernate.validator.metadata.ConstraintDescriptorImpl<?>) constraint; |
| if (constraintHibernate.getElementType() == |
| (useField ? ElementType.FIELD : ElementType.METHOD)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Predicate<PropertyDescriptor> newPropertyNameMatches( |
| final PropertyDescriptor p) { |
| return new Predicate<PropertyDescriptor>() { |
| @Override |
| public boolean apply(PropertyDescriptor input) { |
| return input.getPropertyName().equals(p.getPropertyName()); |
| } |
| }; |
| } |
| |
| private String toWrapperName(JField field) { |
| return "_" + field.getName(); |
| } |
| |
| private String toWrapperName(JMethod method) { |
| return "_" + method.getName(); |
| } |
| |
| private String validateMethodFieldName(PropertyDescriptor p) { |
| return "validateProperty_" + p.getPropertyName(); |
| } |
| |
| private String validateMethodGetterName(PropertyDescriptor p) { |
| return "validateProperty_get" + p.getPropertyName(); |
| } |
| |
| private void writeBeanDescriptor(SourceWriter sw) { |
| BeanDescriptor beanDescriptor = beanHelper.getBeanDescriptor(); |
| |
| // private final GwtBeanDescriptor <MyBean> beanDescriptor = |
| sw.print("private final "); |
| sw.print(GwtBeanDescriptor.class.getCanonicalName()); |
| sw.print("<" + beanHelper.getTypeCanonicalName() + ">"); |
| sw.println(" beanDescriptor = "); |
| sw.indent(); |
| sw.indent(); |
| |
| // GwtBeanDescriptorImpl.builder(Order.class) |
| sw.print(GwtBeanDescriptorImpl.class.getCanonicalName()); |
| sw.println(".builder(" + beanHelper.getTypeCanonicalName() + ".class)"); |
| sw.indent(); |
| sw.indent(); |
| |
| // .setConstrained(true) |
| sw.println(".setConstrained(" + beanDescriptor.isBeanConstrained() + ")"); |
| |
| int count = 0; |
| for (ConstraintDescriptor<?> constraint : beanDescriptor.getConstraintDescriptors()) { |
| if (areConstraintDescriptorGroupsValid(constraint)) { |
| // .add(c0) |
| sw.println(".add(" + constraintDescriptorVar("this", count) + ")"); |
| count++; |
| } |
| } |
| |
| // .put("myProperty", myProperty_pd) |
| for (PropertyDescriptor p : beanDescriptor.getConstrainedProperties()) { |
| sw.print(".put(\""); |
| sw.print(p.getPropertyName()); |
| sw.print("\", "); |
| sw.print(p.getPropertyName()); |
| sw.println("_pd)"); |
| } |
| |
| // .setBeanMetadata(beanMetadata) |
| sw.println(".setBeanMetadata(beanMetadata)"); |
| |
| // .build(); |
| sw.println(".build();"); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| } |
| |
| private void writeBeanMetadata(SourceWriter sw) throws UnableToCompleteException { |
| // private final BeanMetadata beanMetadata = |
| sw.println("private final BeanMetadata beanMetadata ="); |
| sw.indent(); |
| sw.indent(); |
| |
| // new BeanMetadata( |
| sw.println("new " + BeanMetadata.class.getSimpleName() + "("); |
| sw.indent(); |
| sw.indent(); |
| |
| // <<bean class>>, <<default group seq class 1>>, <<default group seq class 2>>, ... |
| Class<?> beanClazz = beanHelper.getClazz(); |
| sw.print(asLiteral(beanClazz)); |
| GroupSequence groupSeqAnnotation = beanClazz.getAnnotation(GroupSequence.class); |
| List<Class<?>> groupSequence = new ArrayList<Class<?>>(); |
| if (groupSeqAnnotation == null) { |
| groupSequence.add(beanClazz); |
| } else { |
| groupSequence.addAll(Arrays.asList(groupSeqAnnotation.value())); |
| } |
| boolean groupSequenceContainsDefault = false; |
| for (Class<?> group : groupSequence) { |
| sw.println(","); |
| if (group.getName().equals(beanClazz.getName())) { |
| sw.print(asLiteral(Default.class)); |
| groupSequenceContainsDefault = true; |
| } |
| else if (group.getName().equals(Default.class.getName())) { |
| throw error(logger, "'Default.class' cannot appear in default group sequence list."); |
| } |
| else { |
| sw.print(asLiteral(group)); |
| } |
| } |
| if (!groupSequenceContainsDefault) { |
| throw error(logger, beanClazz.getName() + " must be part of the redefined default group " + |
| "sequence."); |
| } |
| |
| sw.println(");"); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| } |
| |
| private void writeClassLevelConstraintsValidation(SourceWriter sw, String groupsVarName) |
| throws UnableToCompleteException { |
| // all class level constraints |
| int count = 0; |
| Class<?> clazz = beanHelper.getClazz(); |
| for (ConstraintDescriptor<?> constraint : beanHelper.getBeanDescriptor().getConstraintDescriptors()) { |
| if (areConstraintDescriptorGroupsValid(constraint)) { |
| if (hasMatchingAnnotation(constraint)) { |
| |
| if (!constraint.getConstraintValidatorClasses().isEmpty()) { |
| Class<? extends ConstraintValidator<? extends Annotation, ?>> validatorClass = |
| getValidatorForType(constraint, clazz); |
| |
| // validate(context, violations, null, object, |
| sw.print("validate(context, violations, null, object, "); |
| |
| // new MyValidtor(), |
| sw.print("new "); |
| sw.print(validatorClass.getCanonicalName()); |
| sw.print("(), "); // TODO(nchalko) use ConstraintValidatorFactory |
| |
| // this.aConstraintDescriptor, groups); |
| sw.print(constraintDescriptorVar("this", count)); |
| sw.print(", "); |
| sw.print(groupsVarName); |
| sw.println(");"); |
| } else if (constraint.getComposingConstraints().isEmpty()) { |
| // TODO(nchalko) What does the spec say to do here. |
| logger.log(TreeLogger.WARN, "No ConstraintValidator of " + constraint |
| + " for type " + clazz); |
| } |
| // TODO(nchalko) handle constraint.isReportAsSingleViolation() and |
| // hasComposingConstraints |
| } |
| count++; |
| } |
| } |
| } |
| |
| private void writeConstraintDescriptor(SourceWriter sw, |
| ConstraintDescriptor<? extends Annotation> constraint, |
| ElementType elementType, |
| ConstraintOrigin origin, |
| String constraintDescripotorVar) throws UnableToCompleteException { |
| Class<? extends Annotation> annotationType = |
| constraint.getAnnotation().annotationType(); |
| |
| // First list all composing constraints |
| int count = 0; |
| for (ConstraintDescriptor<?> composingConstraint : |
| constraint.getComposingConstraints()) { |
| writeConstraintDescriptor(sw, composingConstraint, elementType, origin, |
| constraintDescripotorVar + "_" + count++); |
| } |
| |
| // private final ConstraintDescriptorImpl<MyAnnotation> constraintDescriptor = ; |
| sw.print("private final "); |
| sw.print(ConstraintDescriptorImpl.class.getCanonicalName()); |
| sw.print("<"); |
| |
| sw.print(annotationType.getCanonicalName()); |
| sw.print(">"); |
| |
| sw.println(" " + constraintDescripotorVar + " = "); |
| sw.indent(); |
| sw.indent(); |
| |
| // ConstraintDescriptorImpl.<MyConstraint> builder() |
| sw.print(ConstraintDescriptorImpl.class.getCanonicalName()); |
| sw.print(".<"); |
| |
| sw.print(annotationType.getCanonicalName()); |
| sw.println("> builder()"); |
| sw.indent(); |
| sw.indent(); |
| |
| // .setAnnotation(new MyAnnotation ) |
| sw.println(".setAnnotation( "); |
| sw.indent(); |
| sw.indent(); |
| writeNewAnnotation(sw, constraint); |
| sw.println(")"); |
| sw.outdent(); |
| sw.outdent(); |
| |
| // .setAttributes(builder() |
| sw.println(".setAttributes(attributeBuilder()"); |
| sw.indent(); |
| |
| for (Map.Entry<String, Object> entry : |
| constraint.getAttributes().entrySet()) { |
| // .put(key, value) |
| sw.print(".put("); |
| String key = entry.getKey(); |
| sw.print(asLiteral(key)); |
| sw.print(", "); |
| Object value = entry.getValue(); |
| // Add the Default group if it is not already present |
| if ("groups".equals(key) && value instanceof Class[] && ((Class[])value).length == 0) { |
| value = new Class[] { Default.class }; |
| } |
| sw.print(asLiteral(value)); |
| sw.println(")"); |
| } |
| |
| // .build()) |
| sw.println(".build())"); |
| sw.outdent(); |
| |
| // .setConstraintValidatorClasses(classes ) |
| sw.print(".setConstraintValidatorClasses("); |
| sw.print(asLiteral(asArray(constraint.getConstraintValidatorClasses(), |
| new Class[0]))); |
| sw.println(")"); |
| |
| int ccCount = constraint.getComposingConstraints().size(); |
| for (int i = 0; i < ccCount; i++) { |
| // .addComposingConstraint(cX_X) |
| sw.print(".addComposingConstraint("); |
| sw.print(constraintDescripotorVar + "_" + i); |
| sw.println(")"); |
| } |
| |
| // .getGroups(groups) |
| sw.print(".setGroups("); |
| Set<Class<?>> groups = constraint.getGroups(); |
| sw.print(asLiteral(asArray(groups, new Class<?>[0]))); |
| sw.println(")"); |
| |
| // .setPayload(payload) |
| sw.print(".setPayload("); |
| Set<Class<? extends Payload>> payload = constraint.getPayload(); |
| sw.print(asLiteral(asArray(payload, new Class[0]))); |
| sw.println(")"); |
| |
| // .setReportAsSingleViolation(boolean ) |
| sw.print(".setReportAsSingleViolation("); |
| sw.print(Boolean.valueOf(constraint.isReportAsSingleViolation()) |
| .toString()); |
| sw.println(")"); |
| |
| // .setElementType(elementType) |
| sw.print(".setElementType("); |
| sw.print(asLiteral(elementType)); |
| sw.println(")"); |
| |
| // .setDefinedOn(origin) |
| sw.print(".setDefinedOn("); |
| sw.print(asLiteral(origin)); |
| sw.println(")"); |
| |
| // .build(); |
| sw.println(".build();"); |
| sw.outdent(); |
| sw.outdent(); |
| |
| sw.outdent(); |
| sw.outdent(); |
| sw.println(); |
| } |
| |
| private void writeExpandDefaultAndValidate(SourceWriter sw, Stage stage) throws UnableToCompleteException { |
| Class<?> clazz = beanHelper.getClazz(); |
| |
| // ArrayList<Class<?>> justGroups = new ArrayList<Class<?>>(); |
| sw.println("ArrayList<Class<?>> justGroups = new ArrayList<Class<?>>();"); |
| |
| // for (Group g : groups) { |
| sw.println("for (Group g : groups) {"); |
| sw.indent(); |
| // if (!g.isDefaultGroup() || !getBeanMetadata().defaultGroupSequenceIsRedefined()) { |
| sw.println("if (!g.isDefaultGroup() || !getBeanMetadata().defaultGroupSequenceIsRedefined()) {"); |
| sw.indent(); |
| // justGroups.add(g.getGroup()); |
| sw.println("justGroups.add(g.getGroup());"); |
| sw.outdent(); |
| // } |
| sw.println("}"); |
| sw.outdent(); |
| // } |
| sw.println("}"); |
| |
| // Class<?>[] justGroupsArray = justGroups.toArray(new Class<?>[justGroups.size()]); |
| sw.println("Class<?>[] justGroupsArray = justGroups.toArray(new Class<?>[justGroups.size()]);"); |
| |
| switch (stage) { |
| case OBJECT: |
| // validateAllNonInheritedProperties(context, object, violations, justGroupsArray); |
| sw.println("validateAllNonInheritedProperties(context, object, violations, " + |
| "justGroupsArray);"); |
| writeClassLevelConstraintsValidation(sw, "justGroupsArray"); |
| break; |
| case PROPERTY: |
| // validatePropertyGroups(context, object, propertyName, violations, justGroupsArray); |
| sw.println("validatePropertyGroups(context, object, propertyName, violations, " + |
| "justGroupsArray);"); |
| break; |
| case VALUE: |
| // validateValueGroups(context, beanType, propertyName, value, violations, |
| // justGroupsArray); |
| sw.println("validateValueGroups(context, beanType, propertyName, value, violations, " + |
| "justGroupsArray);"); |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| |
| // if (getBeanMetadata().defaultGroupSequenceIsRedefined()) { |
| sw.println("if (getBeanMetadata().defaultGroupSequenceIsRedefined()) {"); |
| sw.indent(); |
| // for (Class<?> g : beanMetadata.getDefaultGroupSequence()) { |
| sw.println("for (Class<?> g : beanMetadata.getDefaultGroupSequence()) {"); |
| sw.indent(); |
| // int numberOfViolations = violations.size(); |
| sw.println("int numberOfViolations = violations.size();"); |
| |
| switch (stage) { |
| case OBJECT: |
| // validateAllNonInheritedProperties(context, object, violations, g); |
| sw.println("validateAllNonInheritedProperties(context, object, violations, g);"); |
| writeClassLevelConstraintsValidation(sw, "g"); |
| // validate super classes and super interfaces |
| writeValidateInheritance(sw, clazz, Stage.OBJECT, null, false, "g"); |
| break; |
| case PROPERTY: |
| // validatePropertyGroups(context, object, propertyName, violations, g); |
| sw.println("validatePropertyGroups(context, object, propertyName, violations, g);"); |
| break; |
| case VALUE: |
| // validateValueGroups(context, beanType, propertyName, value, violations, g); |
| sw.println("validateValueGroups(context, beanType, propertyName, value, violations, g);"); |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| |
| // if (violations.size() > numberOfViolations) { |
| sw.println("if (violations.size() > numberOfViolations) {"); |
| sw.indent(); |
| // break; |
| sw.println("break;"); |
| sw.outdent(); |
| // } |
| sw.println("}"); |
| sw.outdent(); |
| // } |
| sw.println("}"); |
| sw.outdent(); |
| // } |
| sw.println("}"); |
| if (stage == Stage.OBJECT) { |
| // else { |
| sw.println("else {"); |
| sw.indent(); |
| |
| // validate super classes and super interfaces |
| writeValidateInheritance(sw, clazz, Stage.OBJECT, null, true, "groups"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| |
| private void writeExpandDefaultAndValidateClassGroups(SourceWriter sw) throws UnableToCompleteException { |
| // public <T> void expandDefaultAndValidateClassGroups( |
| sw.println("public <T> void expandDefaultAndValidateClassGroups("); |
| |
| // GwtValidationContext<T> context, BeanType object, |
| // Set<ConstraintViolation<T>> violations, Group... groups) { |
| sw.indent(); |
| sw.indent(); |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println(beanHelper.getTypeCanonicalName() + " object,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Group... groups) {"); |
| sw.outdent(); |
| |
| writeExpandDefaultAndValidate(sw, Stage.OBJECT); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeExpandDefaultAndValidatePropertyGroups(SourceWriter sw) throws UnableToCompleteException { |
| // public <T> void expandDefaultAndValidatePropertyGroups( |
| sw.println("public <T> void expandDefaultAndValidatePropertyGroups("); |
| |
| // GwtValidationContext<T> context, BeanType object, String propertyName, |
| // Set<ConstraintViolation<T>> violations, Group... groups) { |
| sw.indent(); |
| sw.indent(); |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println(beanHelper.getTypeCanonicalName() + " object,"); |
| sw.println("String propertyName,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Group... groups) {"); |
| sw.outdent(); |
| |
| writeExpandDefaultAndValidate(sw, Stage.PROPERTY); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeExpandDefaultAndValidateValueGroups(SourceWriter sw) throws UnableToCompleteException { |
| // public <T> void expandDefaultAndValidateValueGroups( |
| sw.println("public <T> void expandDefaultAndValidateValueGroups("); |
| |
| // GwtValidationContext<T> context, Class<Author> beanType, String propertyName, |
| // Object value, Set<ConstraintViolation<T>> violations, Group... groups) { |
| sw.indent(); |
| sw.indent(); |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println("Class<" + beanHelper.getTypeCanonicalName() + "> beanType,"); |
| sw.println("String propertyName,"); |
| sw.println("Object value,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Group... groups) {"); |
| sw.outdent(); |
| |
| writeExpandDefaultAndValidate(sw, Stage.VALUE); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeFields(SourceWriter sw) throws UnableToCompleteException { |
| |
| // Create a static array of all valid property names. |
| BeanInfo beanInfo; |
| try { |
| beanInfo = Introspector.getBeanInfo(beanHelper.getClazz()); |
| } catch (IntrospectionException e) { |
| throw error(logger, e); |
| } |
| |
| // private static final java.util.List<String> ALL_PROPERTY_NAMES = |
| sw.println("private static final java.util.List<String> ALL_PROPERTY_NAMES = "); |
| sw.indent(); |
| sw.indent(); |
| |
| // Collections.<String>unmodifiableList( |
| sw.println("java.util.Collections.<String>unmodifiableList("); |
| sw.indent(); |
| sw.indent(); |
| |
| // java.util.Arrays.<String>asList( |
| sw.print("java.util.Arrays.<String>asList("); |
| |
| // "foo","bar" ); |
| sw.print(Joiner.on(",").join( |
| Iterables.transform( |
| ImmutableList.copyOf(beanInfo.getPropertyDescriptors()), |
| Functions.compose(TO_LITERAL, PROPERTY_DESCRIPTOR_TO_NAME)))); |
| sw.println("));"); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| |
| // Write the metadata for the bean |
| writeBeanMetadata(sw); |
| sw.println(); |
| |
| // Create a variable for each constraint of each property |
| for (PropertyDescriptor p : |
| beanHelper.getBeanDescriptor().getConstrainedProperties()) { |
| int count = 0; |
| for (ConstraintDescriptor<?> constraint : p.getConstraintDescriptors()) { |
| org.hibernate.validator.metadata.ConstraintDescriptorImpl<?> constraintHibernate = |
| (org.hibernate.validator.metadata.ConstraintDescriptorImpl<?>) constraint; |
| if (areConstraintDescriptorGroupsValid(constraint)) { |
| writeConstraintDescriptor(sw, constraint, constraintHibernate.getElementType(), |
| convertConstraintOriginEnum(constraintHibernate.getDefinedOn()), |
| constraintDescriptorVar(p.getPropertyName(), count++)); |
| } |
| } |
| writePropertyDescriptor(sw, p); |
| if (p.isCascaded()) { |
| beansToValidate.add(isIterableOrMap(p.getElementClass()) |
| ? createBeanHelper(beanHelper.getAssociationType(p, true)) |
| : createBeanHelper(p.getElementClass())); |
| } |
| } |
| |
| // Create a variable for each constraint of this class. |
| int count = 0; |
| for (ConstraintDescriptor<?> constraint : |
| beanHelper.getBeanDescriptor().getConstraintDescriptors()) { |
| org.hibernate.validator.metadata.ConstraintDescriptorImpl<?> constraintHibernate = |
| (org.hibernate.validator.metadata.ConstraintDescriptorImpl<?>) constraint; |
| if (areConstraintDescriptorGroupsValid(constraint)) { |
| writeConstraintDescriptor(sw, constraint, ElementType.TYPE, |
| convertConstraintOriginEnum(constraintHibernate.getDefinedOn()), |
| constraintDescriptorVar("this", count++)); |
| } |
| } |
| |
| // Now write the BeanDescriptor after we already have the |
| // PropertyDescriptors and class constraints |
| writeBeanDescriptor(sw); |
| sw.println(); |
| } |
| |
| private void writeFieldWrapperMethod(SourceWriter sw, JField field) { |
| writeUnsafeNativeLongIfNeeded(sw, field.getType()); |
| |
| // private native fieldType _fieldName(com.example.Bean object) /*-{ |
| sw.print("private native "); |
| |
| sw.print(field.getType().getQualifiedSourceName()); |
| sw.print(" "); |
| sw.print(toWrapperName(field)); |
| sw.print("("); |
| sw.print(field.getEnclosingType().getQualifiedSourceName()); |
| sw.println(" object) /*-{"); |
| sw.indent(); |
| |
| // return object.@com.examples.Bean::myMethod(); |
| sw.print("return object.@"); |
| sw.print(field.getEnclosingType().getQualifiedSourceName()); |
| sw.print("::" + field.getName()); |
| sw.println(";"); |
| |
| // }-*/; |
| sw.outdent(); |
| sw.println("}-*/;"); |
| } |
| |
| private void writeGetBeanMetadata(SourceWriter sw) { |
| // public BeanMetadata getBeanMetadata() { |
| sw.println("public BeanMetadata getBeanMetadata() {"); |
| sw.indent(); |
| |
| // return beanMetadata; |
| sw.println("return beanMetadata;"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeGetDescriptor(SourceWriter sw) { |
| // public GwtBeanDescriptor<beanType> |
| // getConstraints(ValidationGroupsMetadata validationGroupsMetadata) { |
| sw.print("public "); |
| sw.print("GwtBeanDescriptor<" + beanHelper.getTypeCanonicalName() + "> "); |
| sw.println("getConstraints(ValidationGroupsMetadata validationGroupsMetadata) {"); |
| sw.indent(); |
| |
| // beanDescriptor.setValidationGroupsMetadata(validationGroupsMetadata); |
| sw.println("beanDescriptor.setValidationGroupsMetadata(validationGroupsMetadata);"); |
| |
| // return beanDescriptor; |
| sw.println("return beanDescriptor;"); |
| |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeGetterWrapperMethod(SourceWriter sw, JMethod method) { |
| writeUnsafeNativeLongIfNeeded(sw, method.getReturnType()); |
| |
| // private native fieldType _getter(Bean object) /*={ |
| sw.print("private native "); |
| sw.print(method.getReturnType().getQualifiedSourceName()); |
| sw.print(" "); |
| sw.print(toWrapperName(method)); |
| sw.print("("); |
| sw.print(beanType.getName()); |
| sw.println(" object) /*-{"); |
| sw.indent(); |
| |
| // return object.@com.examples.Bean::myMethod()(); |
| sw.print("return object."); |
| sw.print(method.getJsniSignature()); |
| sw.println("();"); |
| |
| // }-*/; |
| sw.outdent(); |
| sw.println("}-*/;"); |
| } |
| |
| private void writeIfPropertyNameNotFound(SourceWriter sw) { |
| // if (!ALL_PROPERTY_NAMES.contains(propertyName)) { |
| sw.println(" if (!ALL_PROPERTY_NAMES.contains(propertyName)) {"); |
| sw.indent(); |
| |
| // throw new IllegalArgumentException(propertyName |
| // +"is not a valid property of myClass"); |
| sw.print("throw new "); |
| sw.print(IllegalArgumentException.class.getCanonicalName()); |
| sw.print("( propertyName +\" is not a valid property of "); |
| sw.print(beanType.getQualifiedSourceName()); |
| sw.println("\");"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeNewAnnotation(SourceWriter sw, |
| ConstraintDescriptor<? extends Annotation> constraint) |
| throws UnableToCompleteException { |
| Annotation annotation = constraint.getAnnotation(); |
| Class<? extends Annotation> annotationType = annotation.annotationType(); |
| |
| // new MyAnnotation () { |
| sw.print("new "); |
| sw.print(annotationType.getCanonicalName()); |
| sw.println("(){"); |
| sw.indent(); |
| sw.indent(); |
| |
| // public Class<? extends Annotation> annotationType() { return |
| // MyAnnotation.class; } |
| sw.print("public Class<? extends Annotation> annotationType() { return "); |
| sw.print(annotationType.getCanonicalName()); |
| sw.println(".class; }"); |
| |
| for (Method method : annotationType.getMethods()) { |
| // method.isAbstract would be better |
| if (method.getDeclaringClass().equals(annotation.annotationType())) { |
| // public returnType method() { return value ;} |
| sw.print("public "); |
| sw.print(method.getReturnType().getCanonicalName()); // TODO handle |
| // generics |
| sw.print(" "); |
| sw.print(method.getName()); |
| sw.print("() { return "); |
| |
| try { |
| Object value = method.invoke(annotation); |
| sw.print(asLiteral(value)); |
| } catch (IllegalArgumentException e) { |
| throw error(logger, e); |
| } catch (IllegalAccessException e) { |
| throw error(logger, e); |
| } catch (InvocationTargetException e) { |
| throw error(logger, e); |
| } |
| sw.println(";}"); |
| } |
| } |
| |
| sw.outdent(); |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeNewViolations(SourceWriter sw, String violationName) { |
| // Set<ConstraintViolation<T>> violations = |
| sw.print("Set<ConstraintViolation<T>> "); |
| sw.print(violationName); |
| sw.println(" = "); |
| sw.indent(); |
| sw.indent(); |
| |
| // new HashSet<ConstraintViolation<T>>(); |
| sw.println("new HashSet<ConstraintViolation<T>>();"); |
| sw.outdent(); |
| sw.outdent(); |
| } |
| |
| /** |
| * @param sw |
| * @param p |
| */ |
| private void writePropertyDescriptor(SourceWriter sw, PropertyDescriptor p) { |
| // private final PropertyDescriptor myProperty_pd = |
| sw.print("private final "); |
| sw.print(PropertyDescriptorImpl.class.getCanonicalName()); |
| sw.print(" "); |
| sw.print(p.getPropertyName()); |
| sw.println("_pd ="); |
| sw.indent(); |
| sw.indent(); |
| |
| // new PropertyDescriptorImpl( |
| sw.println("new " + PropertyDescriptorImpl.class.getCanonicalName() + "("); |
| sw.indent(); |
| sw.indent(); |
| |
| // "myProperty", |
| sw.println("\"" + p.getPropertyName() + "\","); |
| |
| // MyType.class, |
| sw.println(p.getElementClass().getCanonicalName() + ".class,"); |
| |
| // isCascaded, |
| sw.print(Boolean.toString(p.isCascaded()) + ","); |
| |
| // beanMetadata, |
| sw.print("beanMetadata"); |
| |
| // myProperty_c0, |
| // myProperty_c1 ); |
| int count = 0; |
| for (ConstraintDescriptor<?> constraint : p.getConstraintDescriptors()) { |
| if (areConstraintDescriptorGroupsValid(constraint)) { |
| sw.println(","); // Print the , for the previous line |
| sw.print(constraintDescriptorVar(p.getPropertyName(), count)); |
| count++; |
| } |
| } |
| sw.println(");"); |
| |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| } |
| |
| private void writePropertyValidators(SourceWriter sw) |
| throws UnableToCompleteException { |
| for (PropertyDescriptor p : |
| beanHelper.getBeanDescriptor().getConstrainedProperties()) { |
| if (beanHelper.hasField(p)) { |
| writeValidatePropertyMethod(sw, p, true); |
| sw.println(); |
| } |
| if (beanHelper.hasGetter(p)) { |
| writeValidatePropertyMethod(sw, p, false); |
| sw.println(); |
| } |
| } |
| } |
| |
| private void writeValidateAllNonInheritedProperties(SourceWriter sw) { |
| // private <T> void validateAllNonInheritedProperties( |
| sw.println("private <T> void validateAllNonInheritedProperties("); |
| sw.indent(); |
| sw.indent(); |
| |
| // GwtValidationContext<T> context, BeanType object, |
| // Set<ConstraintViolation<T>> violations, Class<?>... groups) { |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println(beanHelper.getTypeCanonicalName() + " object,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Class<?>... groups) {"); |
| sw.outdent(); |
| |
| for (PropertyDescriptor p : beanHelper.getBeanDescriptor().getConstrainedProperties()) { |
| writeValidatePropertyCall(sw, p, false, true); |
| } |
| |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeValidateClassGroups(SourceWriter sw) throws UnableToCompleteException { |
| // public <T> void validateClassGroups( |
| sw.println("public <T> void validateClassGroups("); |
| |
| // GwtValidationContext<T> context, BeanType object, |
| // Set<ConstraintViolation<T>> violations, Group... groups) { |
| sw.indent(); |
| sw.indent(); |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println(beanHelper.getTypeCanonicalName() + " object,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Class<?>... groups) {"); |
| sw.outdent(); |
| |
| // /// For each group |
| |
| // TODO(nchalko) handle the sequence in the AbstractValidator |
| |
| // See JSR 303 section 3.5 |
| // all reachable fields |
| // all reachable getters (both) at once |
| // including all reachable and cascadable associations |
| |
| sw.println("validateAllNonInheritedProperties(context, object, violations, groups);"); |
| |
| // validate super classes and super interfaces |
| writeValidateInheritance(sw, beanHelper.getClazz(), Stage.OBJECT, null, false, "groups"); |
| |
| writeClassLevelConstraintsValidation(sw, "groups"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeValidateConstraint(SourceWriter sw, PropertyDescriptor p, |
| Class<?> elementClass, ConstraintDescriptor<?> constraint, |
| String constraintDescriptorVar) throws UnableToCompleteException { |
| writeValidateConstraint(sw, p, elementClass, constraint, |
| constraintDescriptorVar, DEFAULT_VIOLATION_VAR); |
| } |
| |
| /** |
| * Writes the call to actually validate a constraint, including its composite |
| * constraints. |
| * <p> |
| * If the constraint is annotated as |
| * {@link javax.validation.ReportAsSingleViolation ReportAsSingleViolation}, |
| * then is called recursively and the {@code violationsVar} is changed to |
| * match the {@code constraintDescriptorVar}. |
| * |
| * @param sw the Source Writer |
| * @param p the property |
| * @param elementClass The class of the Element |
| * @param constraint the constraint to validate. |
| * @param constraintDescriptorVar the name of the constraintDescriptor |
| * variable. |
| * @param violationsVar the name of the variable to hold violations |
| * @throws UnableToCompleteException |
| */ |
| private void writeValidateConstraint(SourceWriter sw, PropertyDescriptor p, |
| Class<?> elementClass, ConstraintDescriptor<?> constraint, |
| String constraintDescriptorVar, String violationsVar) |
| throws UnableToCompleteException { |
| boolean isComposite = !constraint.getComposingConstraints().isEmpty(); |
| boolean firstReportAsSingleViolation = |
| constraint.isReportAsSingleViolation() |
| && violationsVar.equals(DEFAULT_VIOLATION_VAR) && isComposite; |
| boolean reportAsSingleViolation = firstReportAsSingleViolation |
| || !violationsVar.equals(DEFAULT_VIOLATION_VAR); |
| boolean hasValidator = !constraint.getConstraintValidatorClasses() |
| .isEmpty(); |
| String compositeViolationsVar = constraintDescriptorVar + "_violations"; |
| |
| // Only do this the first time in a constraint composition. |
| if (firstReportAsSingleViolation) { |
| // Report myConstraint as Single Violation |
| sw.print("// Report "); |
| sw.print(constraint.getAnnotation().annotationType().getCanonicalName()); |
| sw.println(" as Single Violation"); |
| writeNewViolations(sw, compositeViolationsVar); |
| } |
| |
| if (hasValidator) { |
| Class<? extends ConstraintValidator<? extends Annotation, ?>> validatorClass; |
| try { |
| validatorClass = getValidatorForType(constraint, elementClass); |
| } catch (UnexpectedTypeException e) { |
| throw error(logger, e); |
| } |
| |
| if (firstReportAsSingleViolation) { |
| // if (! |
| sw.println("if (!"); |
| sw.indent(); |
| sw.indent(); |
| } |
| |
| // validate(myContext, violations object, value, new MyValidator(), |
| // constraintDescriptor, groups)); |
| sw.print("validate(myContext, "); |
| sw.print(violationsVar); |
| sw.print(", object, value, "); |
| sw.print("new "); |
| sw.print(validatorClass.getCanonicalName()); |
| sw.print("(), "); |
| sw.print(constraintDescriptorVar); |
| sw.print(", groups)"); |
| if (firstReportAsSingleViolation) { |
| // ) { |
| sw.println(") {"); |
| sw.outdent(); |
| |
| } else if (!reportAsSingleViolation) { |
| // ; |
| sw.println(";"); |
| } else if (isComposite) { |
| // || |
| sw.println(" ||"); |
| } |
| } else if (!isComposite) { |
| // TODO(nchalko) What does the spec say to do here. |
| logger.log(TreeLogger.WARN, "No ConstraintValidator of " + constraint + " for " |
| + p.getPropertyName() + " of type " + elementClass); |
| } |
| |
| if (firstReportAsSingleViolation) { |
| // if ( |
| sw.print("if ("); |
| sw.indent(); |
| sw.indent(); |
| } |
| int count = 0; |
| |
| for (ConstraintDescriptor<?> compositeConstraint : constraint |
| .getComposingConstraints()) { |
| String compositeVar = constraintDescriptorVar + "_" + count++; |
| writeValidateConstraint(sw, p, elementClass, compositeConstraint, |
| compositeVar, firstReportAsSingleViolation ? compositeViolationsVar |
| : violationsVar); |
| if (!reportAsSingleViolation) { |
| // ; |
| sw.println(";"); |
| } else { |
| // || |
| sw.println(" ||"); |
| } |
| } |
| if (isComposite && reportAsSingleViolation) { |
| // false |
| sw.print("false"); |
| } |
| if (firstReportAsSingleViolation) { |
| // ) { |
| sw.println(" ) {"); |
| sw.outdent(); |
| |
| // addSingleViolation(myContext, violations, object, value, |
| // constraintDescriptor); |
| sw.print("addSingleViolation(myContext, violations, object, value, "); |
| sw.print(constraintDescriptorVar); |
| sw.println(");"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| |
| if (hasValidator) { |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| } |
| |
| private void writeValidateFieldCall(SourceWriter sw, PropertyDescriptor p, |
| boolean useValue, boolean honorValid) { |
| String propertyName = p.getPropertyName(); |
| |
| // validateProperty_<<field>>(context, |
| sw.print(validateMethodFieldName(p)); |
| sw.print("(context, "); |
| sw.print("violations, "); |
| |
| // null, (MyType) value, |
| // or |
| // object, object.getLastName(), |
| if (useValue) { |
| sw.print("null, "); |
| sw.print("("); |
| sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(p, |
| true))); |
| sw.print(") value"); |
| } else { |
| sw.print("object, "); |
| JField field = beanType.getField(propertyName); |
| if (field.isPublic()) { |
| sw.print("object."); |
| sw.print(propertyName); |
| } else { |
| fieldsToWrap.add(field); |
| sw.print(toWrapperName(field) + "(object)"); |
| } |
| } |
| sw.print(", "); |
| |
| // honorValid, groups); |
| sw.print(Boolean.toString(honorValid)); |
| sw.println(", groups);"); |
| } |
| |
| private void writeValidateGetterCall(SourceWriter sw, PropertyDescriptor p, |
| boolean useValue, boolean honorValid) { |
| // validateProperty_get<<field>>(context, violations, |
| sw.print(validateMethodGetterName(p)); |
| sw.print("(context, "); |
| sw.print("violations, "); |
| |
| // object, object.getMyProp(), |
| // or |
| // null, (MyType) value, |
| if (useValue) { |
| sw.print("null, "); |
| sw.print("("); |
| sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(p, |
| false))); |
| sw.print(") value"); |
| } else { |
| sw.print("object, "); |
| JMethod method = beanType.findMethod(asGetter(p), NO_ARGS); |
| if (method.isPublic()) { |
| sw.print("object."); |
| sw.print(asGetter(p)); |
| sw.print("()"); |
| } else { |
| gettersToWrap.add(method); |
| sw.print(toWrapperName(method) + "(object)"); |
| } |
| } |
| sw.print(", "); |
| |
| // honorValid, groups); |
| sw.print(Boolean.toString(honorValid)); |
| sw.println(", groups);"); |
| } |
| |
| private void writeValidateInheritance(SourceWriter sw, Class<?> clazz, Stage stage, |
| PropertyDescriptor property) throws UnableToCompleteException { |
| writeValidateInheritance(sw, clazz, stage, property, false, "groups"); |
| } |
| |
| private void writeValidateInheritance(SourceWriter sw, Class<?> clazz, Stage stage, |
| PropertyDescriptor property, boolean expandDefaultGroupSequence, String groupsVarName) |
| throws UnableToCompleteException { |
| writeValidateInterfaces(sw, clazz, stage, property, expandDefaultGroupSequence, groupsVarName); |
| Class<?> superClass = clazz.getSuperclass(); |
| if (superClass != null) { |
| writeValidatorCall(sw, superClass, stage, property, expandDefaultGroupSequence, groupsVarName); |
| } |
| } |
| |
| private void writeValidateInterfaces(SourceWriter sw, Class<?> clazz, |
| Stage stage, PropertyDescriptor p, boolean expandDefaultGroupSequence, String groupsVarName) |
| throws UnableToCompleteException { |
| for (Class<?> type : clazz.getInterfaces()) { |
| writeValidatorCall(sw, type, stage, p, expandDefaultGroupSequence, groupsVarName); |
| writeValidateInterfaces(sw, type, stage, p, expandDefaultGroupSequence, groupsVarName); |
| } |
| } |
| |
| private void writeValidateIterable(SourceWriter sw, PropertyDescriptor p) { |
| // int i = 0; |
| sw.println("int i = 0;"); |
| |
| // for (Object instance : value) { |
| sw.println("for(Object instance : value) {"); |
| sw.indent(); |
| |
| // if(instance != null && !context.alreadyValidated(instance)) { |
| sw.println(" if(instance != null && !context.alreadyValidated(instance)) {"); |
| sw.indent(); |
| |
| // violations.addAll( |
| sw.println("violations.addAll("); |
| sw.indent(); |
| sw.indent(); |
| |
| // context.getValidator().validate( |
| sw.println("context.getValidator().validate("); |
| sw.indent(); |
| sw.indent(); |
| |
| Class<?> elementClass = p.getElementClass(); |
| if (elementClass.isArray() || List.class.isAssignableFrom(elementClass)) { |
| // context.appendIndex("myProperty",i++), |
| sw.print("context.appendIndex(\""); |
| sw.print(p.getPropertyName()); |
| sw.println("\",i),"); |
| } else { |
| // context.appendIterable("myProperty"), |
| sw.print("context.appendIterable(\""); |
| sw.print(p.getPropertyName()); |
| sw.println("\"),"); |
| } |
| |
| // instance, groups)); |
| sw.println("instance, groups));"); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| |
| // i++; |
| sw.println("i++;"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeValidateMap(SourceWriter sw, PropertyDescriptor p) { |
| // for (Entry<?, Type> entry : value.entrySet()) { |
| sw.print("for("); |
| sw.print(Entry.class.getCanonicalName()); |
| sw.println("<?, ?> entry : value.entrySet()) {"); |
| sw.indent(); |
| |
| // if(entry.getValue() != null && |
| // !context.alreadyValidated(entry.getValue())) { |
| sw.println(" if(entry.getValue() != null && !context.alreadyValidated(entry.getValue())) {"); |
| sw.indent(); |
| |
| // violations.addAll( |
| sw.println("violations.addAll("); |
| sw.indent(); |
| sw.indent(); |
| |
| // context.getValidator().validate( |
| sw.println("context.getValidator().validate("); |
| sw.indent(); |
| sw.indent(); |
| |
| // context.appendKey("myProperty",entry.getKey()), |
| sw.print("context.appendKey(\""); |
| sw.print(p.getPropertyName()); |
| sw.println("\",entry.getKey()),"); |
| |
| // entry.getValue(), groups)); |
| sw.println("entry.getValue(), groups));"); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| sw.outdent(); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeValidatePropertyCall(SourceWriter sw, |
| PropertyDescriptor property, boolean useValue, boolean honorValid) { |
| if (useValue) { |
| // boolean valueTypeMatches = false; |
| sw.println("boolean valueTypeMatches = false;"); |
| } |
| if (beanHelper.hasGetter(property)) { |
| if (useValue) { |
| // if ( value == null || value instanceof propertyType) { |
| sw.print("if ( value == null || value instanceof "); |
| sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType( |
| property, false))); |
| sw.println(") {"); |
| sw.indent(); |
| |
| // valueTypeMatches = true; |
| sw.println("valueTypeMatches = true;"); |
| } |
| // validate_getMyProperty |
| writeValidateGetterCall(sw, property, useValue, honorValid); |
| if (useValue) { |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| |
| if (beanHelper.hasField(property)) { |
| if (useValue) { |
| // if ( value == null || value instanceof propertyType) { |
| sw.print("if ( value == null || value instanceof "); |
| sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType( |
| property, true))); |
| sw.println(") {"); |
| sw.indent(); |
| |
| // valueTypeMatches = true; |
| sw.println("valueTypeMatches = true;"); |
| } |
| // validate_myProperty |
| writeValidateFieldCall(sw, property, useValue, honorValid); |
| if (useValue) { |
| // } else |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| |
| if (useValue & (beanHelper.hasGetter(property) || beanHelper.hasField(property))) { |
| // if(!valueTypeMatches) { |
| sw.println("if(!valueTypeMatches) {"); |
| sw.indent(); |
| |
| // throw new ValidationException(value.getClass + |
| // " is not a valid type for " + propertyName); |
| sw.print("throw new ValidationException"); |
| sw.println("(value.getClass() +\" is not a valid type for \"+ propertyName);"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| |
| private void writeValidatePropertyGroups(SourceWriter sw) |
| throws UnableToCompleteException { |
| // public <T> void validatePropertyGroups( |
| sw.println("public <T> void validatePropertyGroups("); |
| |
| // GwtValidationContext<T> context, BeanType object, String propertyName, |
| // Set<ConstraintViolation<T>> violations, Class<?>... groups) throws ValidationException { |
| sw.indent(); |
| sw.indent(); |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println(beanHelper.getTypeCanonicalName() + " object,"); |
| sw.println("String propertyName,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Class<?>... groups) throws ValidationException {"); |
| sw.outdent(); |
| |
| for (PropertyDescriptor property : beanHelper.getBeanDescriptor().getConstrainedProperties()) { |
| // if (propertyName.equals(myPropety)) { |
| sw.print("if (propertyName.equals(\""); |
| sw.print(property.getPropertyName()); |
| sw.println("\")) {"); |
| sw.indent(); |
| |
| writeValidatePropertyCall(sw, property, false, false); |
| |
| // validate all super classes and interfaces |
| writeValidateInheritance(sw, beanHelper.getClazz(), Stage.PROPERTY, |
| property); |
| |
| // } |
| sw.outdent(); |
| sw.print("} else "); |
| } |
| |
| writeIfPropertyNameNotFound(sw); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeValidatePropertyMethod(SourceWriter sw, |
| PropertyDescriptor p, boolean useField) throws UnableToCompleteException { |
| Class<?> elementClass = p.getElementClass(); |
| JType elementType = beanHelper.getElementType(p, useField); |
| |
| // private final <T> void validateProperty_{get}<p>( |
| sw.print("private final <T> void "); |
| if (useField) { |
| sw.print(validateMethodFieldName(p)); |
| } else { |
| sw.print(validateMethodGetterName(p)); |
| } |
| sw.println("("); |
| sw.indent(); |
| sw.indent(); |
| |
| // final GwtValidationContext<T> context, |
| sw.println("final GwtValidationContext<T> context,"); |
| |
| // final Set<ConstraintViolation<T>> violations, |
| sw.println("final Set<ConstraintViolation<T>> violations,"); |
| |
| // BeanType object, |
| sw.println(beanHelper.getTypeCanonicalName() + " object,"); |
| |
| // final <Type> value, |
| sw.print("final "); |
| sw.print(elementType.getParameterizedQualifiedSourceName()); |
| sw.println(" value,"); |
| |
| // boolean honorValid, |
| sw.println("boolean honorValid,"); |
| |
| // Class<?>... groups) { |
| sw.println("Class<?>... groups) {"); |
| sw.outdent(); |
| |
| // only write the checks if the property is constrained in some way |
| if (isPropertyConstrained(p, useField)) { |
| // context = context.append("myProperty"); |
| sw.print("final GwtValidationContext<T> myContext = context.append(\""); |
| sw.print(p.getPropertyName()); |
| sw.println("\");"); |
| |
| // only check this property if the TraversableResolver says we can |
| |
| // Node leafNode = myContext.getPath().getLeafNode(); |
| sw.println("Node leafNode = myContext.getPath().getLeafNode();"); |
| // PathImpl path = myContext.getPath().getPathWithoutLeafNode(); |
| sw.println("PathImpl path = myContext.getPath().getPathWithoutLeafNode();"); |
| // boolean isReachable; |
| sw.println("boolean isReachable;"); |
| // try { |
| sw.println("try {"); |
| sw.indent(); |
| // isReachable = myContext.getTraversableResolver().isReachable(object, leafNode, |
| // myContext.getRootBeanClass(), path, ElementType); |
| sw.println("isReachable = myContext.getTraversableResolver().isReachable(object, " + |
| "leafNode, myContext.getRootBeanClass(), path, " + |
| (useField ? asLiteral(ElementType.FIELD) : asLiteral(ElementType.METHOD)) + ");"); |
| // } catch (Exception e) { |
| sw.outdent(); |
| sw.println("} catch (Exception e) {"); |
| sw.indent(); |
| // throw new ValidationException("TraversableResolver isReachable caused an exception", e); |
| sw.println("throw new ValidationException(\"TraversableResolver isReachable caused an " + |
| "exception\", e);"); |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| // if (isReachable) { |
| sw.println("if (isReachable) {"); |
| sw.indent(); |
| |
| // TODO(nchalko) move this out of here to the Validate method |
| if (p.isCascaded() && hasValid(p, useField)) { |
| |
| // if (honorValid && value != null) { |
| sw.println("if (honorValid && value != null) {"); |
| sw.indent(); |
| // boolean isCascadable; |
| sw.println("boolean isCascadable;"); |
| // try { |
| sw.println("try {"); |
| sw.indent(); |
| // isCascadable = myContext.getTraversableResolver().isCascadable(object, leafNode, |
| // myContext.getRootBeanClass(), path, ElementType) |
| sw.println("isCascadable = myContext.getTraversableResolver().isCascadable(object, " + |
| "leafNode, myContext.getRootBeanClass(), path, " + |
| (useField ? asLiteral(ElementType.FIELD) : asLiteral(ElementType.METHOD)) + ");"); |
| // } catch (Exception e) { |
| sw.outdent(); |
| sw.println("} catch (Exception e) {"); |
| sw.indent(); |
| // throw new ValidationException("TraversableResolver isReachable caused an exception", e); |
| sw.println("throw new ValidationException(\"TraversableResolver isCascadable caused an " + |
| "exception\", e);"); |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| // if (isCascadable) { |
| sw.println("if (isCascadable) {"); |
| sw.indent(); |
| |
| if (isIterableOrMap(elementClass)) { |
| JClassType associationType = beanHelper.getAssociationType(p, |
| useField); |
| createBeanHelper(associationType); |
| if (Map.class.isAssignableFrom(elementClass)) { |
| writeValidateMap(sw, p); |
| } else { |
| writeValidateIterable(sw, p); |
| } |
| } else { |
| createBeanHelper(elementClass); |
| |
| // if (!context.alreadyValidated(value)) { |
| sw.println(" if (!context.alreadyValidated(value)) {"); |
| sw.indent(); |
| |
| // violations.addAll(myContext.getValidator().validate(context, value, |
| // groups)); |
| sw.print("violations.addAll("); |
| sw.println("myContext.getValidator().validate(myContext, value, groups));"); |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| // It is possible for an annotation with the exact same values to be set on |
| // both the field and the getter. |
| // Keep track of the ones we have used to make sure we don't duplicate. |
| Set<Object> includedAnnotations = Sets.newHashSet(); |
| int count = 0; |
| for (ConstraintDescriptor<?> constraint : p.getConstraintDescriptors()) { |
| if (areConstraintDescriptorGroupsValid(constraint)) { |
| Object annotation = constraint.getAnnotation(); |
| if (hasMatchingAnnotation(p, useField, constraint)) { |
| String constraintDescriptorVar = constraintDescriptorVar(p.getPropertyName(), count); |
| if (!includedAnnotations.contains(annotation)) { |
| if (useField) { |
| writeValidateConstraint(sw, p, elementClass, constraint, constraintDescriptorVar); |
| } else { |
| // The annotation hasn't been looked at twice (yet) and we are validating a getter |
| // Write the call if only the getter has this constraint applied to it |
| boolean hasField = beanHelper.hasField(p); |
| if (!hasField || |
| (hasField && !hasMatchingAnnotation(p, true, constraint))) { |
| writeValidateConstraint(sw, p, elementClass, constraint, constraintDescriptorVar); |
| } |
| } |
| } else { |
| // The annotation has been looked at once already during this validate property call |
| // so we know the field and the getter are both annotated with the same constraint. |
| if (!useField) { |
| writeValidateConstraint(sw, p, elementClass, constraint, constraintDescriptorVar); |
| } |
| } |
| includedAnnotations.add(annotation); |
| } |
| count++; |
| } |
| } |
| // } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| private void writeValidateValueGroups(SourceWriter sw) |
| throws UnableToCompleteException { |
| // public <T> void validateValueGroups( |
| sw.println("public <T> void validateValueGroups("); |
| |
| // GwtValidationContext<T> context, Class<Author> beanType, String propertyName, |
| // Object value, Set<ConstraintViolation<T>> violations, Class<?>... groups) { |
| sw.indent(); |
| sw.indent(); |
| sw.println("GwtValidationContext<T> context,"); |
| sw.println("Class<" + beanHelper.getTypeCanonicalName() + "> beanType,"); |
| sw.println("String propertyName,"); |
| sw.println("Object value,"); |
| sw.println("Set<ConstraintViolation<T>> violations,"); |
| sw.println("Class<?>... groups) {"); |
| sw.outdent(); |
| |
| for (PropertyDescriptor property : |
| beanHelper.getBeanDescriptor().getConstrainedProperties()) { |
| // if (propertyName.equals(myPropety)) { |
| sw.print("if (propertyName.equals(\""); |
| sw.print(property.getPropertyName()); |
| sw.println("\")) {"); |
| sw.indent(); |
| |
| if (!isIterableOrMap(property.getElementClass())) { |
| writeValidatePropertyCall(sw, property, true, false); |
| } |
| |
| // validate all super classes and interfaces |
| writeValidateInheritance(sw, beanHelper.getClazz(), |
| Stage.VALUE, property); |
| |
| // } |
| sw.outdent(); |
| sw.print("} else "); |
| } |
| |
| writeIfPropertyNameNotFound(sw); |
| |
| sw.outdent(); |
| sw.println("}"); |
| } |
| |
| /** |
| * @param p Only used if writing a call to validate a property - otherwise can be null. |
| * @param expandDefaultGroupSequence Only used if writing a call to validate a bean. |
| * @param groupsVarName The name of the variable containing the groups. |
| */ |
| private void writeValidatorCall(SourceWriter sw, Class<?> type, Stage stage, |
| PropertyDescriptor p, boolean expandDefaultGroupSequence, String groupsVarName) |
| throws UnableToCompleteException { |
| if (cache.isClassConstrained(type) && !isIterableOrMap(type)) { |
| BeanHelper helper = createBeanHelper(type); |
| beansToValidate.add(helper); |
| switch (stage) { |
| case OBJECT: |
| // myValidator |
| sw.print(helper.getValidatorInstanceName()); |
| if (expandDefaultGroupSequence) { |
| // .expandDefaultAndValidateClassGroups(context,object,violations,groups); |
| sw.println(".expandDefaultAndValidateClassGroups(context, object, violations, " + |
| groupsVarName + ");"); |
| } else { |
| // .validateClassGroups(context,object,violations,groups); |
| sw.println(".validateClassGroups(context, object, violations, " + groupsVarName + ");"); |
| } |
| break; |
| case PROPERTY: |
| if (isPropertyConstrained(helper, p)) { |
| // myValidator.validatePropertyGroups(context,object |
| // ,propertyName, violations, groups); |
| sw.print(helper.getValidatorInstanceName()); |
| sw.print(".validatePropertyGroups(context, object,"); |
| sw.println(" propertyName, violations, " + groupsVarName + ");"); |
| } |
| break; |
| case VALUE: |
| if (isPropertyConstrained(helper, p)) { |
| // myValidator.validateValueGroups(context,beanType |
| // ,propertyName, value, violations, groups); |
| sw.print(helper.getValidatorInstanceName()); |
| sw.print(".validateValueGroups(context, "); |
| sw.print(helper.getTypeCanonicalName()); |
| sw.println(".class, propertyName, value, violations, " + groupsVarName + ");"); |
| } |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| } |
| } |
| |
| private void writeWrappers(SourceWriter sw) { |
| sw.println("// Write the wrappers after we know which are needed"); |
| for (JField field : fieldsToWrap) { |
| writeFieldWrapperMethod(sw, field); |
| sw.println(); |
| } |
| |
| for (JMethod method : gettersToWrap) { |
| writeGetterWrapperMethod(sw, method); |
| sw.println(); |
| } |
| } |
| } |