Handle Validation of Cascaded Generic properties.
Make GwtValidators Singletons.

[JSR 303 TCK Result] 88 of 257 (34.24%) Pass with 38 Failures and 14 Errors.

Review at http://gwt-code-reviews.appspot.com/1343801

Review by: rchandia@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9678 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/validation/client/impl/AbstractGwtValidator.java b/user/src/com/google/gwt/validation/client/impl/AbstractGwtValidator.java
index 17da692..1f0ad4f 100644
--- a/user/src/com/google/gwt/validation/client/impl/AbstractGwtValidator.java
+++ b/user/src/com/google/gwt/validation/client/impl/AbstractGwtValidator.java
@@ -20,6 +20,7 @@
 import java.util.Set;
 
 import javax.validation.ConstraintValidatorFactory;
+import javax.validation.ConstraintViolation;
 import javax.validation.MessageInterpolator;
 import javax.validation.TraversableResolver;
 import javax.validation.ValidationException;
@@ -58,6 +59,10 @@
     throw new ValidationException();
   }
 
+  public abstract <T> Set<ConstraintViolation<T>> validate(
+      GwtValidationContext<T> context, Object object, Class<?>... groups)
+      throws ValidationException;
+
   protected void checkGroups(Class<?>... groups) {
     // an empty list of valid groups means all groups are valid.
     if (!validGroups.isEmpty()
diff --git a/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java b/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java
index 0ff46f5..612813f 100644
--- a/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java
+++ b/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java
@@ -32,15 +32,17 @@
   private PathImpl path = new PathImpl();
   private final T rootBean;
   private final MessageInterpolator messageInterpolator;
+  private final AbstractGwtValidator validator;
 
   /**
    *
    */
   public GwtValidationContext(T rootBean, BeanDescriptor beanDescriptor,
-      MessageInterpolator messageInterpolator) {
+      MessageInterpolator messageInterpolator, AbstractGwtValidator validator) {
     this.rootBean = rootBean;
     this.beanDescriptor = beanDescriptor;
     this.messageInterpolator = messageInterpolator;
+    this.validator = validator;
   }
 
   /**
@@ -51,7 +53,7 @@
    */
   public GwtValidationContext<T> append(String name) {
     GwtValidationContext<T> temp = new GwtValidationContext<T>(rootBean,
-        beanDescriptor, messageInterpolator);
+        beanDescriptor, messageInterpolator, validator);
     temp.path = path.append(name);
     return temp;
   }
@@ -64,7 +66,7 @@
    */
   public GwtValidationContext<T> appendIndex(String name, int index) {
     GwtValidationContext<T> temp = new GwtValidationContext<T>(rootBean,
-        beanDescriptor, messageInterpolator);
+        beanDescriptor, messageInterpolator, validator);
     temp.path = path.appendIndex(name, index);
     return temp;
   }
@@ -77,7 +79,7 @@
    */
   public GwtValidationContext<T> appendKey(String name, String key) {
     GwtValidationContext<T> temp = new GwtValidationContext<T>(rootBean,
-        beanDescriptor, messageInterpolator);
+        beanDescriptor, messageInterpolator, validator);
     temp.path = path.appendKey(name, key);
     return temp;
   }
@@ -99,4 +101,8 @@
   public Class<T> getRootBeanClass() {
     return (Class<T>) rootBean.getClass();
   }
+
+  public AbstractGwtValidator getValidator() {
+    return validator;
+  }
 }
diff --git a/user/src/com/google/gwt/validation/rebind/AbstractCreator.java b/user/src/com/google/gwt/validation/rebind/AbstractCreator.java
index 3e02b60..4e79099 100644
--- a/user/src/com/google/gwt/validation/rebind/AbstractCreator.java
+++ b/user/src/com/google/gwt/validation/rebind/AbstractCreator.java
@@ -20,7 +20,6 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JPackage;
-import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.user.rebind.AbstractSourceCreator;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
@@ -65,11 +64,12 @@
 
   protected abstract void compose(ClassSourceFileComposerFactory composerFactory);
 
-  protected BeanHelper createBeanHelper(Class<?> clazz) {
+  protected BeanHelper createBeanHelper(Class<?> clazz)
+      throws UnableToCompleteException {
     return BeanHelper.createBeanHelper(clazz, logger, context);
   }
 
-  protected BeanHelper createBeanHelper(JType jType)
+  protected BeanHelper createBeanHelper(JClassType jType)
       throws UnableToCompleteException {
     return BeanHelper.createBeanHelper(jType, logger, context);
   }
@@ -92,9 +92,9 @@
     sw.indent();
     sw.indent();
 
-    // GWT.create(MyBeanValidator.class);
-    sw.println("GWT.create(" + bean.getFullyQualifiedValidatorName()
-        + ".class);");
+    // MyBeanValidator.INSTANCE;
+    sw.print(bean.getFullyQualifiedValidatorName());
+    sw.println(".INSTANCE;");
     sw.outdent();
     sw.outdent();
   }
diff --git a/user/src/com/google/gwt/validation/rebind/BeanHelper.java b/user/src/com/google/gwt/validation/rebind/BeanHelper.java
index f87a6e9..aaee68e 100644
--- a/user/src/com/google/gwt/validation/rebind/BeanHelper.java
+++ b/user/src/com/google/gwt/validation/rebind/BeanHelper.java
@@ -15,11 +15,18 @@
  */
 package com.google.gwt.validation.rebind;
 
+import com.google.gwt.core.client.GWT;
 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.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
 import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.thirdparty.guava.common.base.Function;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
 import com.google.gwt.validation.client.impl.GwtSpecificValidator;
@@ -32,6 +39,7 @@
 import javax.validation.Validation;
 import javax.validation.Validator;
 import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
 
 /**
  * A simple struct for the various values associated with a Bean that can be
@@ -39,6 +47,13 @@
  */
 final class BeanHelper {
 
+  public static final Function<BeanHelper, Class<?>> TO_CLAZZ = 
+      new Function<BeanHelper, Class<?>>() {
+    public Class<?> apply(BeanHelper helper) {
+      return helper.getClazz();
+    }
+  };
+
   private static final Validator serverSideValidor = Validation.buildDefaultValidatorFactory().getValidator();
 
   // stash the map in a ThreadLocal, since each GWT module lives in its own
@@ -51,28 +66,27 @@
     }
   };
 
-  protected static BeanHelper createBeanHelper(Class<?> clazz,
-      TreeLogger logger, GeneratorContext context) {
-    JClassType beanType = context.getTypeOracle().findType(
-        clazz.getCanonicalName());
-    BeanHelper helper = getBeanHelper(beanType);
-    if (helper == null) {
-      helper = new BeanHelper(beanType, clazz,
-          serverSideValidor.getConstraintsForClass(clazz));
-      addBeanHelper(helper);
-      writeInterface(context, logger, helper);
-    }
-    return helper;
+  public static Map<JClassType, BeanHelper> getBeanHelpers() {
+    return Collections.unmodifiableMap(threadLocalHelperMap.get());
   }
 
-  protected static BeanHelper createBeanHelper(JType jType, TreeLogger logger,
+  protected static BeanHelper createBeanHelper(Class<?> clazz,
+      TreeLogger logger, GeneratorContext context)
+      throws UnableToCompleteException {
+    JClassType beanType = context.getTypeOracle().findType(
+        clazz.getCanonicalName());
+    return createBeanHelper(clazz, beanType, logger, context);
+  }
+
+  protected static BeanHelper createBeanHelper(JClassType jType, TreeLogger logger,
       GeneratorContext context) throws UnableToCompleteException {
+    JClassType erasedType = jType.getErasedType();
     try {
-      Class<?> clazz = Class.forName(jType.getQualifiedSourceName());
-      return createBeanHelper(clazz, logger, context);
+      Class<?> clazz = Class.forName(erasedType.getQualifiedBinaryName());
+      return createBeanHelper(clazz, erasedType, logger, context);
     } catch (ClassNotFoundException e) {
-      logger.log(TreeLogger.ERROR, "Unable to create BeanHelper for " + jType,
-          e);
+      logger.log(TreeLogger.ERROR, "Unable to create BeanHelper for "
+          + erasedType, e);
       throw new UnableToCompleteException();
     }
   }
@@ -82,7 +96,7 @@
   }
 
   static BeanHelper getBeanHelper(JClassType beanType) {
-    return getBeanHelpers().get(beanType);
+    return getBeanHelpers().get(beanType.getErasedType());
   }
 
   /**
@@ -101,8 +115,17 @@
           bean.getPackage(), bean.getValidatorName());
       factory.addImplementedInterface(GwtSpecificValidator.class.getCanonicalName()
           + " <" + bean.getTypeCanonicalName() + ">");
+      factory.addImport(GWT.class.getCanonicalName());
       factory.makeInterface();
       SourceWriter sw = factory.createSourceWriter(context, pw);
+
+      // static MyValidator INSTANCE = GWT.create(MyValidator.class);
+      sw.print("static ");
+      sw.print(bean.getValidatorName());
+      sw.print(" INSTANCE = GWT.create(");
+      sw.print(bean.getValidatorName());
+      sw.println(".class);");
+
       sw.commit(interfaceLogger);
       pw.close();
     }
@@ -112,8 +135,45 @@
     threadLocalHelperMap.get().put(helper.getJClass(), helper);
   }
 
-  private static Map<JClassType, BeanHelper> getBeanHelpers() {
-    return Collections.unmodifiableMap(threadLocalHelperMap.get());
+  private static BeanHelper createBeanHelper(Class<?> clazz,
+      JClassType beanType, TreeLogger logger, GeneratorContext context)
+      throws UnableToCompleteException {
+    BeanHelper helper = getBeanHelper(beanType);
+    if (helper == null) {
+      helper = new BeanHelper(beanType, clazz,
+          serverSideValidor.getConstraintsForClass(clazz));
+      addBeanHelper(helper);
+      writeInterface(context, logger, helper);
+      // now recurse on all Cascaded elements
+      for (PropertyDescriptor p : helper.getBeanDescriptor().getConstrainedProperties()) {
+        if (p.isCascaded()) {
+          createBeanHelper(p, helper, logger, context);
+        }
+      }
+    }
+    return helper;
+  }
+
+  private static void createBeanHelper(PropertyDescriptor p, BeanHelper parent,
+      TreeLogger logger, GeneratorContext context)
+      throws UnableToCompleteException {
+    Class<?> elementClass = p.getElementClass();
+    if (GwtSpecificValidatorCreator.isIterableOrMap(elementClass)) {
+      if (parent.hasField(p)) {
+        JClassType type = parent.getAssociationType(p, true);
+
+        createBeanHelper(type.getErasedType(), logger, context);
+      }
+      if (parent.hasGetter(p)) {
+        JClassType type = parent.getAssociationType(p, false);
+
+        createBeanHelper(type.getErasedType(), logger, context);
+      }
+    } else {
+      if (serverSideValidor.getConstraintsForClass(elementClass).isBeanConstrained()) {
+        createBeanHelper(elementClass, logger, context);
+      }
+    }
   }
 
   private final BeanDescriptor beanDescriptor;
@@ -130,6 +190,25 @@
     this.clazz = clazz;
   }
 
+  public  JClassType getAssociationType(PropertyDescriptor p,
+      boolean useField) {
+    JType type = this.getElementType(p, useField);
+    JArrayType jArray = type.isArray();
+    if (jArray != null) {
+      return jArray.getComponentType().isClassOrInterface();
+    }
+    JParameterizedType pType = type.isParameterized();
+    JClassType[] typeArgs;
+    if (pType == null) {
+      JRawType rType = type.isRawType();
+      typeArgs = rType.getGenericType().getTypeParameters();
+    } else {
+      typeArgs = pType.getTypeArgs();
+    }
+    // it is either a Iterable or a Map use the last type arg.
+    return typeArgs[typeArgs.length - 1].isClassOrInterface();
+  }
+
   public BeanDescriptor getBeanDescriptor() {
     return beanDescriptor;
   }
@@ -175,6 +254,30 @@
     return getTypeCanonicalName();
   }
 
+  JType getElementType(PropertyDescriptor p, boolean useField) {
+    if (useField) {
+      return jClass.findField(p.getPropertyName()).getType();
+    } else {
+      return jClass.findMethod(GwtSpecificValidatorCreator.asGetter(p),
+          GwtSpecificValidatorCreator.NO_ARGS).getReturnType();
+    }
+  }
+
+  boolean hasField(PropertyDescriptor p) {
+    JField field = jClass.findField(p.getPropertyName());
+    return field != null;
+  }
+
+  boolean hasGetter(PropertyDescriptor p) {
+    JType[] paramTypes = new JType[]{};
+    try {
+      jClass.getMethod(GwtSpecificValidatorCreator.asGetter(p), paramTypes);
+      return true;
+    } catch (NotFoundException e) {
+      return false;
+    }
+  }
+
   private String makeJavaSafe(String in) {
     return in.replaceAll("\\.", "_");
   }
diff --git a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
index c839b5e..d16aa69 100644
--- a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
+++ b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
@@ -22,14 +22,11 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.TreeLogger.Type;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.JArrayType;
 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.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.thirdparty.guava.common.base.Function;
@@ -86,10 +83,10 @@
 
   private static final Annotation[] NO_ANNOTATIONS = new Annotation[]{};
 
-  private static final JType[] NO_ARGS = new JType[]{};
+  static final JType[] NO_ARGS = new JType[]{};
 
-  private static Function<java.beans.PropertyDescriptor, String> 
-      PROPERTY_DESCRIPTOR_TO_NAME = 
+  private static Function<java.beans.PropertyDescriptor, String>
+      PROPERTY_DESCRIPTOR_TO_NAME =
           new Function<java.beans.PropertyDescriptor, String>() {
     public String apply(java.beans.PropertyDescriptor pd) {
       return pd.getName();
@@ -103,6 +100,10 @@
     }
   };
 
+  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.
@@ -156,6 +157,26 @@
           + " is 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();
+    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);
+  }
   static <T extends Annotation> Class<?> getTypeOfConstraintValidator(
       Class<? extends ConstraintValidator<T, ?>> constraintClass) {
 
@@ -169,7 +190,6 @@
     throw new IllegalStateException(
         "ConstraintValidators must have a isValid method");
   }
-
   // Visible for testing
   static <A extends Annotation> Class<? extends ConstraintValidator<A, ?>> getValidatorForType(
       Class<?> type,
@@ -207,16 +227,16 @@
     return getValidatorForType(clazz, constraintValidatorClasses);
   }
 
-  private BeanHelper beanHelper;
-  private Set<BeanHelper> beansToValidate = Sets.newHashSet();
+  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 TypeOracle oracle;
 
   public GwtSpecificValidatorCreator(JClassType validatorType,
@@ -279,24 +299,6 @@
     return collection.toArray(array);
   }
 
-  private String asGetter(PropertyDescriptor p) {
-    return "get" + capitalizeFirstLetter(p.getPropertyName());
-  }
-
-  private String capitalizeFirstLetter(String propertyName) {
-    if (propertyName == null) {
-      return null;
-    }
-    if (propertyName.length() == 0) {
-      return "";
-    }
-    String cap = propertyName.substring(0, 1).toUpperCase();
-    if (propertyName.length() > 1) {
-      cap += propertyName.substring(1);
-    }
-    return cap;
-  }
-
   private String constraintDescriptorVar(String name, int count) {
     String s = name + "_c" + count;
     return s;
@@ -340,58 +342,12 @@
     return NO_ANNOTATIONS;
   }
 
-  private JType getAssociationType(PropertyDescriptor p, boolean useField) {
-    JType type = getElementType(p, useField);
-    JArrayType jArray = type.isArray();
-    if (jArray != null) {
-      // TODO(nchalko) check stuff
-      return jArray.getComponentType();
-    }
-    JParameterizedType pType = type.isParameterized();
-    JClassType[] typeArgs = pType.getTypeArgs();
-    // it is either a Iterable or a Map use the last type arg.
-    return typeArgs[typeArgs.length - 1];
-  }
-
-
-  private JType getElementType(PropertyDescriptor p, boolean useField) {
-    if (useField) {
-      return beanType.findField(p.getPropertyName()).getType();
-    } else {
-      return beanType.findMethod(this.asGetter(p), NO_ARGS).getReturnType();
-    }
-  }
-
-  /**
-   * @param elementType
-   * @return
-   */
   private String getQualifiedSourceNonPrimitiveType(JType elementType) {
     JPrimitiveType primitive = elementType.isPrimitive();
     return primitive == null ? elementType.getQualifiedSourceName()
         : primitive.getQualifiedBoxedSourceName();
   }
 
-  private boolean hasField(PropertyDescriptor p) {
-    JField field = beanType.findField(p.getPropertyName());
-    return field != null;
-  }
-
-  /**
-   * @param beanHelper2
-   * @param p
-   * @return
-   */
-  private boolean hasGetter(PropertyDescriptor p) {
-    JType[] paramTypes = new JType[]{};
-    try {
-      beanType.getMethod(asGetter(p), paramTypes);
-      return true;
-    } catch (NotFoundException e) {
-      return false;
-    }
-  }
-
   private boolean hasMatchingAnnotation(Annotation expectedAnnotation,
       Annotation[] annotations) throws UnableToCompleteException {
     // See spec section 2.2. Applying multiple constraints of the same type
@@ -453,13 +409,6 @@
     return getAnnotation(p, useField, Valid.class) != null;
   }
 
-  private boolean isIterableOrMap(Class<?> elementClass) {
-    // TODO(nchalko) handle iterables everywhere this is called.
-    return elementClass.isArray()
-        || Iterable.class.isAssignableFrom(elementClass)
-        || Map.class.isAssignableFrom(elementClass);
-  }
-
   private boolean isPropertyConstrained(BeanHelper helper, PropertyDescriptor p) {
     Set<PropertyDescriptor> propertyDescriptors =
         helper.getBeanDescriptor().getConstrainedProperties();
@@ -695,7 +644,7 @@
       writePropertyDescriptor(sw, p);
       if (p.isCascaded()) {
         beansToValidate.add(isIterableOrMap(p.getElementClass())
-            ? createBeanHelper(getAssociationType(p, true))
+            ? createBeanHelper(beanHelper.getAssociationType(p, true))
             : createBeanHelper(p.getElementClass()));
       }
     }
@@ -901,11 +850,11 @@
       throws UnableToCompleteException {
     for (PropertyDescriptor p :
         beanHelper.getBeanDescriptor().getConstrainedProperties()) {
-      if (hasField(p)) {
+      if (beanHelper.hasField(p)) {
         writeValidatePropertyMethod(sw, p, true);
         sw.println();
       }
-      if (hasGetter(p)) {
+      if (beanHelper.hasGetter(p)) {
         writeValidatePropertyMethod(sw, p, false);
         sw.println();
       }
@@ -930,69 +879,63 @@
 
     writeNewViolations(sw);
 
-    if (beanHelper.getBeanDescriptor().isBeanConstrained()) {
+    // /// For each group
 
-      // /// For each group
+    // TODO(nchalko) handle the sequence in the AbstractValidator
 
-      // 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
 
-      // See JSR 303 section 3.5
-      // all reachable fields
-      // all reachable getters (both) at once
+    Set<PropertyDescriptor> properties = beanHelper.getBeanDescriptor().getConstrainedProperties();
 
-      Set<PropertyDescriptor> properties = beanHelper.getBeanDescriptor().getConstrainedProperties();
-
-      for (PropertyDescriptor p : properties) {
-        writeValidatePropertyCall(sw, p, false);
-      }
-
-      // all class level constraints
-      // including super classes
-      // including super interfaces
-
-      int count = 0;
-      Class<?> clazz = beanHelper.getClazz();
-      for (ConstraintDescriptor<?> constraint : beanHelper.getBeanDescriptor().findConstraints().getConstraintDescriptors()) {
-        Annotation annotation = constraint.getAnnotation();
-        if (hasMatchingAnnotation(constraint)) {
-          Class<? extends ConstraintValidator<? extends Annotation, ?>> validatorClass = getValidatorForType(
-              constraint, clazz);
-          if (validatorClass != null) {
-            // TODO(nchalko) handle constraint.isReportAsSingleViolation() and
-            // hasComposingConstraints
-
-            // validate(context, violations, null, object,
-            sw.print("validate(context, violations, null, object, ");
-
-            // new MyValidtor();
-            sw.print("new ");
-            sw.print(validatorClass.getCanonicalName());
-            sw.print("(), "); // new one each time because validators are not
-                              // thread
-                              // safe
-
-            // this.aConstraintDescriptor, groups);;
-            sw.print(constraintDescriptorVar("this", count));
-            sw.println(", groups);");
-          } else {
-            // TODO(nchalko) What does the spec say to do here.
-            logger.log(Type.WARN, "No ConstraintValidator of " + constraint
-                + " for type " + clazz);
-          }
-        }
-        count++;
-      }
-
-      // validate all super classes and interfaces
-      writeValidateInheritance(sw, clazz, Stage.OBJECT, null);
-
-      // all reachable and cascadable associations
+    for (PropertyDescriptor p : properties) {
+      writeValidatePropertyCall(sw, p, false);
     }
+
+    // all class level constraints
+
+
+    int count = 0;
+    Class<?> clazz = beanHelper.getClazz();
+    for (ConstraintDescriptor<?> constraint : beanHelper.getBeanDescriptor().getConstraintDescriptors()) {
+      if (hasMatchingAnnotation(constraint)) {
+        Class<? extends ConstraintValidator<? extends Annotation, ?>> validatorClass = getValidatorForType(
+            constraint, clazz);
+        if (validatorClass != null) {
+          // TODO(nchalko) handle constraint.isReportAsSingleViolation() and
+          // hasComposingConstraints
+
+          // validate(context, violations, null, object,
+          sw.print("validate(context, violations, null, object, ");
+
+          // new MyValidtor();
+          sw.print("new ");
+          sw.print(validatorClass.getCanonicalName());
+          sw.print("(), "); // new one each time because validators are not
+                            // thread
+                            // safe
+
+          // this.aConstraintDescriptor, groups);;
+          sw.print(constraintDescriptorVar("this", count));
+          sw.println(", groups);");
+        } else {
+          // TODO(nchalko) What does the spec say to do here.
+          logger.log(Type.WARN, "No ConstraintValidator of " + constraint
+              + " for type " + clazz);
+        }
+      }
+      count++;
+    }
+
+    // validate super classes and super interfaces
+    writeValidateInheritance(sw, clazz, Stage.OBJECT, null);
+
     // return violations;
     sw.println("return violations;");
 
-    writeCatchUnexpectedException(
-        sw,
+    writeCatchUnexpectedException(sw,
         "\"Error validating " + beanHelper.getTypeCanonicalName() + "\"");
 
     sw.outdent();
@@ -1045,7 +988,8 @@
     if (useValue) {
       sw.print("null, ");
       sw.print("(");
-      sw.print(getQualifiedSourceNonPrimitiveType(getElementType(p, true)));
+      sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(p,
+          true)));
       sw.print(") value");
     } else {
       sw.print("object, ");
@@ -1077,7 +1021,8 @@
     if (useValue) {
       sw.print("null, ");
       sw.print("(");
-      sw.print(getQualifiedSourceNonPrimitiveType(getElementType(p, false)));
+      sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(p,
+          false)));
       sw.print(") value");
     } else {
       sw.print("object, ");
@@ -1098,7 +1043,8 @@
   }
 
   private void writeValidateInheritance(SourceWriter sw, Class<?> clazz,
-      Stage stage, PropertyDescriptor property) {
+      Stage stage, PropertyDescriptor property)
+      throws UnableToCompleteException {
     writeValidateInterfaces(sw, clazz, stage, property);
     Class<?> superClass = clazz.getSuperclass();
     if (superClass != null) {
@@ -1107,22 +1053,19 @@
   }
 
   private void writeValidateInterfaces(SourceWriter sw, Class<?> clazz,
-      Stage stage, PropertyDescriptor p) {
+      Stage stage, PropertyDescriptor p) throws UnableToCompleteException {
     for (Class<?> type : clazz.getInterfaces()) {
       writeValidatorCall(sw, type, stage, p);
       writeValidateInterfaces(sw, type, stage, p);
     }
   }
 
-  private void writeValidateIterable(SourceWriter sw, PropertyDescriptor p,
-      JType associationType, BeanHelper helper) {
+  private void writeValidateIterable(SourceWriter sw, PropertyDescriptor p) {
     // int i = 0;
     sw.println("int i = 0;");
 
-    // for (BeanType instance : value) {
-    sw.print("for(");
-    sw.print(associationType.getQualifiedSourceName());
-    sw.println(" instance : value) {");
+    // for (Object instance : value) {
+    sw.print("for(Object instance : value) {");
     sw.indent();
 
     // if(instance != null ) {
@@ -1134,9 +1077,8 @@
     sw.indent();
     sw.indent();
 
-    // myGwtValidator.validate(
-    sw.print(helper.getValidatorInstanceName());
-    sw.println(".validate(");
+    // context.getValidator().validate(
+    sw.println("context.getValidator().validate(");
     sw.indent();
     sw.indent();
 
@@ -1161,14 +1103,11 @@
     sw.println("}");
   }
 
-  private void writeValidateMap(SourceWriter sw, PropertyDescriptor p,
-      JType associationType, BeanHelper helper) {
+  private void writeValidateMap(SourceWriter sw, PropertyDescriptor p) {
     // for (Entry<?, Type> entry : value.entrySet()) {
     sw.print("for(");
     sw.print(Entry.class.getCanonicalName());
-    sw.print("<?, ");
-    sw.print(associationType.getQualifiedSourceName());
-    sw.println("> entry : value.entrySet()) {");
+    sw.print("<?, ?> entry : value.entrySet()) {");
     sw.indent();
 
     // if(entry.getValue() != null ) {
@@ -1180,9 +1119,8 @@
     sw.indent();
     sw.indent();
 
-    // myGwtValidator.validate(
-    sw.print(helper.getValidatorInstanceName());
-    sw.println(".validate(");
+    // context.getValidator().validate(
+    sw.println("context.getValidator().validate(");
     sw.indent();
     sw.indent();
 
@@ -1207,7 +1145,8 @@
     sw.println("}");
   }
 
-  private void writeValidateProperty(SourceWriter sw) {
+  private void writeValidateProperty(SourceWriter sw)
+      throws UnableToCompleteException {
     // public <T> Set<ConstraintViolation<T>> validate(
     sw.println("public <T> Set<ConstraintViolation<T>> validateProperty(");
 
@@ -1262,12 +1201,12 @@
 
   private void writeValidatePropertyCall(SourceWriter sw,
       PropertyDescriptor property, boolean useValue) {
-    if (hasGetter(property)) {
+    if (beanHelper.hasGetter(property)) {
       if (useValue) {
         // if ( value == null || value instanceof propertyType) {
         sw.print("if ( value == null || value instanceof ");
-        sw.print(getQualifiedSourceNonPrimitiveType(getElementType(property,
-            false)));
+        sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(
+            property, false)));
         sw.println(") {");
         sw.indent();
       }
@@ -1280,13 +1219,12 @@
       }
     }
 
-
-    if (hasField(property)) {
+    if (beanHelper.hasField(property)) {
       if (useValue) {
         // if ( value == null || value instanceof propertyType) {
         sw.print("if ( value == null || value instanceof ");
-        sw.print(getQualifiedSourceNonPrimitiveType(getElementType(property,
-            true)));
+        sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(
+            property, true)));
         sw.println(") {");
         sw.indent();
       }
@@ -1299,7 +1237,7 @@
       }
     }
 
-    if (useValue & (hasGetter(property) || hasField(property))) {
+    if (useValue & (beanHelper.hasGetter(property) || beanHelper.hasField(property))) {
       // {
       sw.println(" {");
       sw.indent();
@@ -1318,7 +1256,7 @@
   private void writeValidatePropertyMethod(SourceWriter sw,
       PropertyDescriptor p, boolean useField) throws UnableToCompleteException {
     Class<?> elementClass = p.getElementClass();
-    JType elementType = getElementType(p, useField);
+    JType elementType = beanHelper.getElementType(p, useField);
 
     // private final <T> void validateProperty_{get}<p>(
     sw.print("private final <T> void ");
@@ -1361,21 +1299,21 @@
 
       if (isIterableOrMap(elementClass)) {
         if (hasValid(p, useField)) {
-          JType associationType = getAssociationType(p, useField);
-          BeanHelper helper = createBeanHelper(associationType);
-          // TODO(nchalko) assume iterable for now
+          JClassType associationType = beanHelper.getAssociationType(p,
+              useField);
+          createBeanHelper(associationType);
           if (Map.class.isAssignableFrom(elementClass)) {
-            writeValidateMap(sw, p, associationType, helper);
+            writeValidateMap(sw, p);
           } else {
-            writeValidateIterable(sw, p, associationType, helper);
+            writeValidateIterable(sw, p);
           }
         }
       } else {
-        BeanHelper helper = createBeanHelper(elementClass);
-        // violations.addAll(myGwtValidator.validate(context, value, groups));
+        createBeanHelper(elementClass);
+        // violations.addAll(myContext.getValidator().validate(context, value,
+        // groups));
         sw.print("violations.addAll(");
-        sw.print(helper.getValidatorInstanceName());
-        sw.println(".validate(myContext, value, groups));");
+        sw.println("myContext.getValidator().validate(myContext, value, groups));");
       }
 
       // }
@@ -1398,7 +1336,8 @@
     sw.println("}");
   }
 
-  private void writeValidateValue(SourceWriter sw) {
+  private void writeValidateValue(SourceWriter sw)
+      throws UnableToCompleteException {
     // public <T> Set<ConstraintViolation<T>> validate(
     sw.println("public <T> Set<ConstraintViolation<T>> validateValue(");
 
@@ -1455,7 +1394,7 @@
   }
 
   private void writeValidatorCall(SourceWriter sw, Class<?> type, Stage stage,
-      PropertyDescriptor p) {
+      PropertyDescriptor p) throws UnableToCompleteException {
     if (BeanHelper.isClassConstrained(type) && !isIterableOrMap(type)) {
       BeanHelper helper = createBeanHelper(type);
       beansToValidate.add(helper);
diff --git a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java
index 11ec398..10c0448 100644
--- a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java
+++ b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -49,13 +49,12 @@
     JClassType gwtSpecificInterface = getGwtSpecificValidator(logger, validator);
     JClassType beanType = getBeanType(logger, validator, gwtSpecificInterface);
 
-    BeanHelper beanHelper = BeanHelper.getBeanHelper(beanType);
+    BeanHelper beanHelper = BeanHelper.createBeanHelper(beanType,logger,context);
 
     if (beanHelper == null) {
-      logger.log(TreeLogger.ERROR, "Unable to find BeanHelper for " + beanType
+      logger.log(TreeLogger.ERROR, "Unable to create BeanHelper for " + beanType
           + " " + GwtSpecificValidator.class.getSimpleName()
-          + " should only be referenced from a class created by "
-          + ValidatorGenerator.class.getCanonicalName(), null);
+          + ".", null);
       throw new UnableToCompleteException();
     }
 
diff --git a/user/src/com/google/gwt/validation/rebind/Util.java b/user/src/com/google/gwt/validation/rebind/Util.java
new file mode 100644
index 0000000..b0616ab
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/Util.java
@@ -0,0 +1,118 @@
+/*
+ * 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.thirdparty.guava.common.base.Function;
+import com.google.gwt.thirdparty.guava.common.base.Functions;
+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.Lists;
+import com.google.gwt.thirdparty.guava.common.collect.Ordering;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Static utilities for the validation rebind package.
+ */
+class Util {
+
+  /**
+   * Creates a Predicate that returns false if source contains an associated
+   * class that is a super type of the class associated with the tested T.
+   * 
+   * @param <T> the type to test
+   * @param source the Set of <T> to look for class matches.
+   * @param toClass Function from T to Class
+   * @return newly create predicate.
+   */
+  static <T> Predicate<T> createMostSpecificMatchPredicate(final Set<T> source,
+      final Function<T, Class<?>> toClass) {
+    return new Predicate<T>() {
+
+      public boolean apply(T input) {
+        Class<?> inputClass = toClass.apply(input);
+        for (Class<?> match : Iterables.transform(source, toClass)) {
+          if (!inputClass.equals(match) && inputClass.isAssignableFrom(match)) {
+            return false;
+          }
+        }
+        return true;
+      }
+    };
+  }
+
+  /**
+   * Selects first only the classes that are assignable from the target, and
+   * then returns the most specific matching classes.
+   * 
+   * @param target the Class to match
+   * @param availableClasses classes to search
+   * @return Set of only the most specific classes that match the target.
+   */
+  static Set<Class<?>> findBestMatches(Class<?> target,
+      Set<Class<?>> availableClasses) {
+    Set<Class<?>> matches = new HashSet<Class<?>>();
+    if (availableClasses.contains(target)) {
+      return ImmutableSet.<Class<?>> of(target);
+    } else {
+      for (Class<?> clazz : availableClasses) {
+        if (clazz.isAssignableFrom(target)) {
+          matches.add(clazz);
+        }
+      }
+    }
+    Predicate<Class<?>> moreSpecificClassPredicate = createMostSpecificMatchPredicate(
+        matches, Functions.<Class<?>> identity());
+    return Sets.filter(matches, moreSpecificClassPredicate);
+  }
+
+  /**
+   * Returns a Immutable List sorted with the most specific associated class
+   * first. Each element is guaranteed to not be assignable to any element that
+   * appears before it in the list.
+   */
+  static <T> ImmutableList<T> sortMostSpecificFirst(Iterable<T> classes,
+      Function<T, Class<?>> toClass) {
+    Set<T> working = Sets.newHashSet(classes);
+    List<T> sorted = Lists.newArrayList();
+    Predicate<T> mostSpecific = createMostSpecificMatchPredicate(working, toClass);
+    boolean changed = false;
+    do {
+      changed = false;
+      for (T t : Ordering.usingToString().sortedCopy(working)) {
+        if (mostSpecific.apply(t)) {
+          sorted.add(t);
+          working.remove(t);
+          changed = true;
+        }
+      }
+    } while (changed);
+    if (!working.isEmpty()) {
+      throw new IllegalStateException(
+          "Unable to find a element that does not have a more specific element in the set "
+              + working);
+    }
+    return ImmutableList.copyOf(sorted);
+  }
+
+  private Util() {
+  }
+}
diff --git a/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
index a2a92cf..33e75fe 100644
--- a/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
+++ b/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
@@ -18,6 +18,7 @@
 import com.google.gwt.core.client.GWT;
 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.thirdparty.guava.common.collect.Lists;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
@@ -40,7 +41,7 @@
 public class ValidatorCreator extends AbstractCreator {
 
   /**
-   * The beans to validate in source declaratoin order.
+   * The beans to validate in source declaration order.
    */
   private final List<BeanHelper> beansToValidate = Lists.newArrayList();
   private final GwtValidation gwtValidation;
@@ -49,7 +50,7 @@
   public ValidatorCreator(JClassType validatorType, //
       GwtValidation gwtValidation, //
       TreeLogger logger, //
-      GeneratorContext context) {
+      GeneratorContext context) throws UnableToCompleteException {
     super(context, logger, validatorType);
     this.gwtValidation = gwtValidation;
 
@@ -76,17 +77,17 @@
 
   @Override
   protected void writeClassBody(SourceWriter sourceWriter) {
-      writeTypeSupport(sourceWriter);
-      sourceWriter.println();
-      writeConstructor(sourceWriter);
-      sourceWriter.println();
-      writeValidate(sourceWriter);
-      sourceWriter.println();
-      writeValidateProperty(sourceWriter);
-      sourceWriter.println();
-      writeValidateValue(sourceWriter);
-      sourceWriter.println();
-      writeGetConstraintsForClass(sourceWriter);
+    writeConstructor(sourceWriter);
+    sourceWriter.println();
+    writeValidate(sourceWriter);
+    sourceWriter.println();
+    writeValidateProperty(sourceWriter);
+    sourceWriter.println();
+    writeValidateValue(sourceWriter);
+    sourceWriter.println();
+    writeGetConstraintsForClass(sourceWriter);
+    sourceWriter.println();
+    writeGwtValidate(sourceWriter);
   }
 
   private String getSimpleName() {
@@ -117,17 +118,28 @@
 
 
   private void writeContext(SourceWriter sw, BeanHelper bean, String objectName) {
-    // GwtValidationContext<T> context =
-    // new GwtValidationContext<T>(object,myBeanValidator.getConstraints(),
-    // getMessageInterpolator());
+    // GwtValidationContext<T> context = new GwtValidationContext<T>(
     sw.print(GwtValidationContext.class.getSimpleName());
     sw.print("<T> context =");
     sw.print("    new " + GwtValidationContext.class.getSimpleName());
-    sw.print("<T>(" + objectName + ", ");
-    sw.print(bean.getValidatorInstanceName());
-    sw.print(".getConstraints(), ");
-    sw.print("getMessageInterpolator()");
-    sw.println(");");
+    sw.println("<T>" + "(");
+    sw.indent();
+    sw.indent();
+
+    // object,
+    sw.println(objectName + ", ");
+
+    // MyBeanValidator.INSTANCE.getConstraints(),
+    sw.print(bean.getFullyQualifiedValidatorName());
+    sw.println(".INSTANCE.getConstraints(), ");
+
+    // getMessageInterpolator(),
+    sw.println("getMessageInterpolator(), ");
+
+    // this);
+    sw.println("this);");
+    sw.outdent();
+    sw.outdent();
   }
 
   private void writeGetConstraintsForClass(SourceWriter sw) {
@@ -155,9 +167,61 @@
     sw.println("if (clazz.equals(" + bean.getTypeCanonicalName() + ".class)) {");
     sw.indent();
 
-    // return myBeanValidator.getConstraints();
+    // return MyBeanValidator.INSTANCE.getConstraints();
     sw.print("return ");
-    sw.print(bean.getValidatorInstanceName() + ".getConstraints();");
+    sw.print(bean.getFullyQualifiedValidatorName());
+    sw.println(".INSTANCE.getConstraints();");
+
+    // }
+    sw.outdent();
+    sw.println("}");
+  }
+
+  private void writeGwtValidate(SourceWriter sw) {
+    // public <T> Set<ConstraintViolation<T>> validate(GwtValidationContext<T>
+    // context,
+    sw.print("public <T> Set<ConstraintViolation<T>> ");
+    sw.println("validate(GwtValidationContext<T> context,");
+    sw.indent();
+    sw.indent();
+
+    // Object object, Class<?>... groups) {
+    sw.println("Object object, Class<?>... groups) {");
+    sw.outdent();
+
+    sw.println("checkNotNull(context, \"context\");");
+    sw.println("checkNotNull(object, \"object\");");
+    sw.println("checkNotNull(groups, \"groups\");");
+    sw.println("checkGroups(groups);");
+
+    for (BeanHelper bean : Util.sortMostSpecificFirst(
+        BeanHelper.getBeanHelpers().values(), BeanHelper.TO_CLAZZ)) {
+      writeGwtValidate(sw, bean);
+    }
+
+    // TODO(nchalko) log warning instead.
+    writeThrowIllegalArgumnet(sw, "object.getClass().getName()");
+
+    sw.outdent();
+    sw.println("}");
+  }
+
+  private void writeGwtValidate(SourceWriter sw, BeanHelper bean) {
+    writeIfInstanceofBeanType(sw, bean);
+    sw.indent();
+
+    // return PersonValidator.INSTANCE
+
+    sw.print("return ");
+    sw.println(bean.getFullyQualifiedValidatorName() + ".INSTANCE");
+    sw.indent();
+    sw.indent();
+    // .validate(context, (<<MyBean>>) object, groups);
+    sw.print(".validate(context, ");
+    sw.print("(" + bean.getTypeCanonicalName() + ") object, ");
+    sw.println("groups);");
+    sw.outdent();
+    sw.outdent();
 
     // }
     sw.outdent();
@@ -192,11 +256,6 @@
     sourceWriter.outdent();
   }
 
-  private void writeTypeSupport(SourceWriter sw) {
-    for (BeanHelper bean : beansToValidate) {
-      writeValidatorInstance(sw, bean);
-    }
-  }
 
   private void writeValidate(SourceWriter sw) {
     // public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>...
@@ -224,13 +283,20 @@
 
     writeContext(sw, bean, "object");
 
-    // return personValidator.validate(context, (<<MyBean>>) object, groups);
+    // return PersonValidator.INSTANCE
     sw.print("return ");
-    sw.print(bean.getValidatorInstanceName() + ".validate(");
-    sw.print("context, ");
+    sw.println(bean.getFullyQualifiedValidatorName() + ".INSTANCE");
+    sw.indent();
+    sw.indent();
+
+    // .validate(context, (<<MyBean>>) object, groups);
+    sw.print(".validate(context, ");
     sw.print("(" + bean.getTypeCanonicalName() + ") object, ");
     sw.println("groups);");
+    sw.outdent();
+    sw.outdent();
 
+    // }
     sw.outdent();
     sw.println("}");
   }
@@ -258,11 +324,23 @@
     writeIfInstanceofBeanType(sw, bean);
     sw.indent();
     writeContext(sw, bean, "object");
-    sw.print("return " + bean.getValidatorInstanceName()
-        + ".validateProperty(context, (" + bean.getTypeCanonicalName()
-        + ") object, propertyName, ");
+
+    // return PersonValidator.INSTANCE
+    sw.print("return ");
+    sw.println(bean.getFullyQualifiedValidatorName() + ".INSTANCE");
+    sw.indent();
+    sw.indent();
+
+    // .validateProperty(context, (MyBean) object, propertyName, groups);
+    sw.print(".validateProperty(context, (");
+    sw.print(bean.getTypeCanonicalName());
+    sw.print(") object, propertyName, ");
     sw.println("groups);");
-    sw.outdent(); // if
+    sw.outdent();
+    sw.outdent();
+
+    // }
+    sw.outdent();
     sw.println("}");
   }
 
@@ -290,10 +368,23 @@
         + ".class)) {");
     sw.indent();
     writeContext(sw, bean, "null");
-    sw.println("return " + bean.getValidatorInstanceName()
-        + ".validateValue(context, (Class<" + bean.getTypeCanonicalName()
-        + ">)beanType, propertyName, value, groups);");
-    sw.outdent(); // if
+
+    // return PersonValidator.INSTANCE
+    sw.print("return ");
+    sw.println(bean.getFullyQualifiedValidatorName() + ".INSTANCE");
+    sw.indent();
+    sw.indent();
+
+    // .validateValue(context, (Class<MyBean> beanType, propertyName, value,
+    // groups);
+    sw.print(".validateValue(context, (Class<");
+    sw.print(bean.getTypeCanonicalName());
+    sw.println(">)beanType, propertyName, value, groups);");
+    sw.outdent();
+    sw.outdent();
+
+    // }
+    sw.outdent();
     sw.println("}");
   }
 }
diff --git a/user/test/com/google/gwt/validation/RebindJreSuite.java b/user/test/com/google/gwt/validation/RebindJreSuite.java
new file mode 100644
index 0000000..0929b60
--- /dev/null
+++ b/user/test/com/google/gwt/validation/RebindJreSuite.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import com.google.gwt.validation.rebind.GwtSpecificValidatorCreatorTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * All validation client non GWT tests.
+ */
+public class RebindJreSuite {
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite(
+        "Test suite for validation rebind code that does not require GWT.");
+    suite.addTestSuite(GwtSpecificValidatorCreatorTest.class);
+    return suite;
+  }
+}
diff --git a/user/test/com/google/gwt/validation/rebind/UtilTest.java b/user/test/com/google/gwt/validation/rebind/UtilTest.java
new file mode 100644
index 0000000..f55f07c
--- /dev/null
+++ b/user/test/com/google/gwt/validation/rebind/UtilTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 static com.google.gwt.validation.rebind.Util.findBestMatches;
+
+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.collect.ImmutableList;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests for {@link Util}.
+ */
+public class UtilTest extends TestCase {
+
+  private class Alice {
+  }
+
+  private class Bob {
+  }
+
+  private class Bobby extends Bob {
+  }
+  private class Bobby2 extends Bobby {
+  }
+
+  private interface C1 {
+  }
+
+  private interface C2 {
+  }
+
+  private class Chuck implements C1, C2 {
+  }
+
+  private final static Function<Class<?>, Class<?>> classIdentity = Functions.identity();
+
+  private static void assertContentsInOrder(List<Class<?>> actual,
+      Class<?>... classes) {
+    assertEquals(ImmutableList.copyOf(classes), actual);
+  }
+
+  private static ImmutableSet<Class<?>> of(Class<?>... classes) {
+    return ImmutableSet.copyOf(classes);
+  }
+
+  public void testBestMatches_Bobby2() {
+    Set<Class<?>> actual = findBestMatches(Bobby2.class,
+        of(Alice.class, Bob.class, Bobby.class));
+    assertEquals(1, actual.size());
+    assertEquals(Bobby.class, Iterables.get(actual, 0));
+  }
+
+  public void testBestMatches_none() {
+    Set<Class<?>> actual = Util.findBestMatches(Bob.class, of(Alice.class));
+    assertEquals(0, actual.size());
+  }
+
+  public void testBestMatches_one() {
+    Set<Class<?>> actual = findBestMatches(Bob.class,
+        of(Alice.class, Bob.class));
+    assertEquals(1, actual.size());
+    assertEquals(Bob.class, Iterables.get(actual, 0));
+  }
+
+  public void testBestMatches_two() {
+    Set<Class<?>> actual = findBestMatches(Chuck.class, of(C1.class, C2.class));
+    assertEquals(2, actual.size());
+  }
+
+  public void testSortMostSpecificFirst_chuck() {
+
+    List<Class<?>> actual = Util.sortMostSpecificFirst(
+        of(C2.class, C1.class, Chuck.class), classIdentity);
+    assertContentsInOrder(actual, Chuck.class, C1.class, C2.class);
+  }
+
+  public void testSortMostSpecificFirst_one() {
+    List<Class<?>> actual = Util.sortMostSpecificFirst(of(Alice.class),
+        classIdentity);
+    assertContentsInOrder(actual, Alice.class);
+  }
+}
diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java
index 6c4b4ba..9528c98 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java
@@ -43,11 +43,13 @@
   @Failing(issue = 5882)
   public void testComposedConstraints() {
     delegate.testComposedConstraints();
+    fail("This test only fails on IE.  Forcing it to fail here to keep the counts even. See issue 5882.");
   }
 
   @Failing(issue = 5882)
   public void testComposedConstraintsAreRecursive() {
     delegate.testComposedConstraintsAreRecursive();
+    fail("This test only fails on IE.  Forcing it to fail here to keep the counts even. See issue 5882.");
   }
 
   @Failing(issue = 5799)
diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorGwtTest.java
index 5cf8ba3..0ac0d6e 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/customconstraint/CustomConstraintValidatorGwtTest.java
@@ -33,6 +33,7 @@
   @Failing(issue = 5882)
   public void testDefaultPropertyPath() {
     delegate.testDefaultPropertyPath();
+    fail("This test only fails on IE.  Forcing it to fail here to keep the counts even. See issue 5882.");
   }
 
   @Failing(issue = 5800)
diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionGwtTest.java
index 9989ef2..30f85f4 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/ValidatorResolutionGwtTest.java
@@ -37,6 +37,7 @@
   @Failing(issue = 5882)
   public void testResolutionOfMinMaxForDifferentTypes() {
     delegate.testResolutionOfMinMaxForDifferentTypes();
+    fail("This test only fails on IE.  Forcing it to fail here to keep the counts even. See issue 5882.");
   }
 
   @Failing(issue = 5806)
@@ -56,7 +57,6 @@
     delegate.testTargetTypeIsClass();
   }
 
-  @Failing(issue = 5806)
   public void testTargetTypeIsInterface() {
     delegate.testTargetTypeIsInterface();
   }
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java
index e870ffc..4a1615c 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java
@@ -32,8 +32,9 @@
     delegate.testPropertyPathTraversedObject();
   }
 
-  @Failing(issue = 5803)
+  @Failing(issue = 5982)
   public void testPropertyPathWithArray() {
+    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testPropertyPathWithArray();
   }
 
@@ -41,8 +42,9 @@
     delegate.testPropertyPathWithConstraintViolationForRootObject();
   }
 
-  @Failing(issue = 5803)
+  @Failing(issue = 5982)
   public void testPropertyPathWithList() {
+    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testPropertyPathWithList();
   }
 
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java
index e280916..613a9ad 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java
@@ -30,18 +30,19 @@
     delegate.testConstraintDescriptorWithoutExplicitGroup();
   }
 
-  @Failing(issue = 5882)
   public void testConstraintViolation() {
     delegate.testConstraintViolation();
   }
 
-  @Failing(issue = 5930)
+  @Failing(issue = 5982)
   public void testGraphValidationWithArray() {
+    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testGraphValidationWithArray();
   }
 
-  @Failing(issue = 5930)
+  @Failing(issue = 5982)
   public void testGraphValidationWithList() {
+    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testGraphValidationWithList();
   }
 
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/ValidatePropertyGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/validation/ValidatePropertyGwtTest.java
index 93bb063..74505c9 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/ValidatePropertyGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/ValidatePropertyGwtTest.java
@@ -49,6 +49,7 @@
   @Failing(issue = 5882)
   public void testValidateProperty() {
     delegate.testValidateProperty();
+    fail("This test only fails on IE.  Forcing it to fail here to keep the counts even. See issue 5882.");
   }
 
   public void testValidatePropertyWithEmptyProperty() {
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/GraphNavigationGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/GraphNavigationGwtTest.java
index 7ff07fe..032d939 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/GraphNavigationGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/GraphNavigationGwtTest.java
@@ -20,7 +20,7 @@
 import org.hibernate.jsr303.tck.util.client.Failing;
 
 /**
- * Test wrapper for {@link ValidationTest}.
+ * Test wrapper for {@link GraphNavigationTest}.
  */
 public class GraphNavigationGwtTest extends GWTTestCase {
 
@@ -31,7 +31,6 @@
     return "org.hibernate.jsr303.tck.tests.validation.graphnavigation.TckTest";
   }
 
-  @Failing(issue = 5946)
   public void testContainedIterable() {
     delegate.testContainedIterable();
   }
@@ -41,7 +40,6 @@
     delegate.testContainedMap();
   }
 
-  @Failing(issue = 5946)
   public void testContainedSet() {
     delegate.testContainedSet();
   }
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java b/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java
index 0bdf158..1f6e2ca 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java
@@ -31,8 +31,10 @@
    * Marker Interface for {@link GWT#create(Class)}.
    */
   @GwtValidation(value = {
-      AnimalCaretaker.class, Elephant.class, Parent.class,
-      SingleCage.class, Zoo.class})
+      AnimalCaretaker.class, Elephant.class, Condor.class, GameReserve.class,
+      Parent.class,
+      MultiCage.class, MultiCage.class, SingleCage.class, Zebra.class,
+      Zoo.class})
   public static interface GwtValidator extends Validator {
   }