Public:  First pass at generating a GWT Validator.
This stub never returns any violations.

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

Review by: robertvawter@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8730 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/eclipse/user/.classpath b/eclipse/user/.classpath
index be7278b..e160a17 100644
--- a/eclipse/user/.classpath
+++ b/eclipse/user/.classpath
@@ -36,7 +36,8 @@
 	<classpathentry kind="var" path="GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-r5940.jar" sourcepath="/GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-r5940-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-core-js-r5940.jar" sourcepath="/GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-core-js-r5940-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/redist/json/r2_20080312/json-1.5.jar" sourcepath="/GWT_TOOLS/redist/json/r2_20080312/json-src.jar"/>
-	<classpathentry eported="true" kind="var" path="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA.jar" sourcepath="/GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA-sources.jar"/>
+	<classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA.jar" sourcepath="/GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA-sources.jar"/>
+	<classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-6.1.11.jar" sourcepath="/GWT_TOOLS/lib/jetty/jetty-6.1.11-src.zip"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/samples/validation/src/com/google/gwt/sample/validation/Validation.gwt.xml b/samples/validation/src/com/google/gwt/sample/validation/Validation.gwt.xml
index 120c92d..8b74b57 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/Validation.gwt.xml
+++ b/samples/validation/src/com/google/gwt/sample/validation/Validation.gwt.xml
@@ -16,6 +16,7 @@
   <inherits name='com.google.gwt.rpc.RPC'/>
   <inherits name='com.google.gwt.user.User'/>
   <inherits name='com.google.gwt.user.theme.standard.Standard'/>
+  <inherits name='com.google.gwt.validation.Validation'/>
 
   <entry-point class='com.google.gwt.sample.validation.client.Validation'/>
 
diff --git a/samples/validation/src/com/google/gwt/sample/validation/client/SampleValidator.java b/samples/validation/src/com/google/gwt/sample/validation/client/SampleValidator.java
new file mode 100644
index 0000000..14f8bb5
--- /dev/null
+++ b/samples/validation/src/com/google/gwt/sample/validation/client/SampleValidator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.sample.validation.client;
+
+import com.google.gwt.sample.validation.shared.Person;
+import com.google.gwt.validation.client.GwtValidation;
+
+import javax.validation.Validator;
+
+/**
+ * Validator marker for the Valiation Sample project. Only the classes listed in
+ * the {@link GwtValidation} annotation can be validated.
+ */
+@GwtValidation(Person.class)
+public interface SampleValidator extends Validator {
+}
diff --git a/samples/validation/src/com/google/gwt/sample/validation/client/ValidationView.java b/samples/validation/src/com/google/gwt/sample/validation/client/ValidationView.java
index 37635e0..21a90f7 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/client/ValidationView.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/client/ValidationView.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
@@ -33,6 +33,11 @@
 import com.google.gwt.user.client.ui.TextBox;
 import com.google.gwt.user.client.ui.Widget;
 
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+
 /**
  * Display the Validation sample.
  */
@@ -118,8 +123,20 @@
     errorLabel.setText("");
     person.setName(nameField.getText());
 
-    // TODO(nchalko) validate.
+    Validator validator = GWT.create(SampleValidator.class);
 
+    Set<ConstraintViolation<Person>> violations = validator.validate(person);
+    if (!violations.isEmpty()) {
+      StringBuffer errorMessage = new StringBuffer();
+      for (ConstraintViolation<Person> constraintViolation : violations) {
+        if (errorMessage.length() == 0) {
+          errorMessage.append('\n');
+        }
+        errorMessage.append(constraintViolation.getMessage());
+      }
+      errorLabel.setText(errorMessage.toString());
+      return;
+    }
     sendButton.setEnabled(false);
     textToServer.setText(person.getName());
     serverResponse.setText("");
diff --git a/samples/validation/war/WEB-INF/classes/marker b/samples/validation/war/WEB-INF/classes/marker
deleted file mode 100644
index e69de29..0000000
--- a/samples/validation/war/WEB-INF/classes/marker
+++ /dev/null
diff --git a/user/src/com/google/gwt/validation/Validation.gwt.xml b/user/src/com/google/gwt/validation/Validation.gwt.xml
index 6314a0b..ce2ab6d 100644
--- a/user/src/com/google/gwt/validation/Validation.gwt.xml
+++ b/user/src/com/google/gwt/validation/Validation.gwt.xml
@@ -16,8 +16,18 @@
   the License.
 -->
 <module>
-   <inherits name="com.google.gwt.user.User"/>
-   <inherits name="com.google.gwt.regexp.RegExp"/>
-   <inherits name="javax.validation.Validation"/>
-   <source path="client"/>
+  <inherits name="com.google.gwt.user.User" />
+  <inherits name="com.google.gwt.regexp.RegExp" />
+  <inherits name="javax.validation.Validation" />
+  <source path="client" />
+
+  <!-- Generators -->
+  <generate-with class="com.google.gwt.validation.rebind.ValidatorGenerator">
+    <when-type-assignable class="javax.validation.Validator" />
+  </generate-with>
+  <generate-with
+    class="com.google.gwt.validation.rebind.GwtSpecificValidatorGenerator">
+    <when-type-assignable
+      class="com.google.gwt.validation.client.impl.GwtSpecificValidator" />
+  </generate-with>
 </module>
diff --git a/user/src/com/google/gwt/validation/client/impl/AbstractGwtSpecificValidator.java b/user/src/com/google/gwt/validation/client/impl/AbstractGwtSpecificValidator.java
index abb74ea..90109c5 100644
--- a/user/src/com/google/gwt/validation/client/impl/AbstractGwtSpecificValidator.java
+++ b/user/src/com/google/gwt/validation/client/impl/AbstractGwtSpecificValidator.java
@@ -36,7 +36,8 @@
       G object, V value, ConstraintValidator<A, V> validator,
       ConstraintDescriptorImpl<A> constraintDescriptor, Class<?>[] groups) {
     validator.initialize(constraintDescriptor.getAnnotation());
-    ConstraintValidatorContextImpl<A, V> constraintValidatorContext = context.createConstraintValidatorContext(constraintDescriptor);
+    ConstraintValidatorContextImpl<A, V> constraintValidatorContext =
+        context.createConstraintValidatorContext(constraintDescriptor);
     if (!validator.isValid(value, constraintValidatorContext)) {
       addViolations(//
           context, //
@@ -81,4 +82,4 @@
         .build();
     return violation;
   }
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/gwt/validation/rebind/AbstractCreator.java b/user/src/com/google/gwt/validation/rebind/AbstractCreator.java
new file mode 100644
index 0000000..5a165ac
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/AbstractCreator.java
@@ -0,0 +1,93 @@
+/*
+ * 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.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JPackage;
+import com.google.gwt.user.rebind.AbstractSourceCreator;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+
+import java.io.PrintWriter;
+
+/**
+ * Abstract Class for Creating source files.
+ * <p>
+ * This class is not thread safe.
+ */
+public abstract class AbstractCreator extends AbstractSourceCreator {
+
+  protected final GeneratorContext context;
+
+  protected final TreeLogger logger;
+
+  protected final JClassType validatorType;
+
+  public AbstractCreator(GeneratorContext context, TreeLogger logger,
+      JClassType validatorType) {
+    super();
+    this.context = context;
+    this.logger = branch(logger, "Creating " + validatorType);
+    this.validatorType = validatorType;
+  }
+
+  public final String create() {
+    SourceWriter sourceWriter = getSourceWriter(logger, context);
+    if (sourceWriter != null) {
+      writeClassBody(sourceWriter);
+      sourceWriter.commit(logger);
+    }
+    return getQaulifiedName();
+  }
+
+  protected abstract void compose(ClassSourceFileComposerFactory composerFactory);
+
+  protected final String getPackage() {
+    JPackage serviceIntfPkg = validatorType.getPackage();
+    String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
+    return packageName;
+  }
+
+  protected abstract void writeClassBody(SourceWriter sourceWriter);
+
+  private final String getQaulifiedName() {
+    String packageName = getPackage();
+    return (packageName == "" ? "" : packageName + ".") + getSimpleName();
+  }
+
+  private final String getSimpleName() {
+    return validatorType.getSimpleSourceName() + "Impl";
+  }
+
+  private final SourceWriter getSourceWriter(TreeLogger logger,
+      GeneratorContext ctx) {
+    String packageName = getPackage();
+    String simpleName = getSimpleName();
+    PrintWriter printWriter = ctx.tryCreate(logger, packageName, simpleName);
+    if (printWriter == null) {
+      return null;
+    }
+
+    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
+        packageName, simpleName);
+    compose(composerFactory);
+    SourceWriter sourceWriter = composerFactory.createSourceWriter(ctx,
+        printWriter);
+    return sourceWriter;
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/validation/rebind/BeanHelper.java b/user/src/com/google/gwt/validation/rebind/BeanHelper.java
new file mode 100644
index 0000000..97e52ea
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/BeanHelper.java
@@ -0,0 +1,54 @@
+/*
+ * 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.ext.typeinfo.JClassType;
+
+/**
+ * A simple struct for the various values associated with a Bean that can be
+ * validated.
+ */
+final class BeanHelper {
+
+  private final JClassType jClass;
+
+  public BeanHelper(JClassType jClass) {
+    super();
+    this.jClass = jClass;
+  }
+
+  public String getDescriptorName() {
+
+    return jClass.getName() + "Descriptor";
+  }
+
+  public String getTypeCanonicalName() {
+    return jClass.getQualifiedSourceName();
+  }
+
+  public String getValidatorInstanceName() {
+    return jClass.getName().toLowerCase() + "Validator";
+  }
+
+  public String getValidatorName() {
+    return jClass.getName() + "Validator";
+  }
+
+  @Override
+  public String toString() {
+    return getTypeCanonicalName();
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
new file mode 100644
index 0000000..be6098a
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
@@ -0,0 +1,192 @@
+/*
+ * 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.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+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.GwtBeanDescriptor;
+import com.google.gwt.validation.client.impl.GwtValidationContext;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.metadata.BeanDescriptor;
+
+/**
+ * Creates a {@link com.google.gwt.validation.client.GwtSpecificValidator}.
+ * <p>
+ * This class is not thread safe.
+ */
+
+public class GwtSpecificValidatorCreator extends AbstractCreator {
+
+  final JClassType beanType;
+  private BeanHelper beanHelper;
+  private final TypeOracle oracle;
+
+  public GwtSpecificValidatorCreator(JClassType validatorType,
+      JClassType beanType, BeanHelper beanHelper, TreeLogger logger,
+      GeneratorContext context) {
+    super(context, logger, validatorType);
+    this.oracle = context.getTypeOracle();
+    this.beanType = beanType;
+    this.beanHelper = beanHelper;
+  }
+
+  @Override
+  protected void compose(ClassSourceFileComposerFactory composerFactory) {
+    Class<?>[] imports = new Class<?>[]{
+        GWT.class,
+        GwtBeanDescriptor.class,
+        GwtValidationContext.class,
+        Set.class,
+        HashSet.class,
+        ConstraintViolation.class,
+        BeanDescriptor.class,
+        };
+    for (Class<?> imp : imports) {
+      composerFactory.addImport(imp.getCanonicalName());
+    }
+
+    composerFactory.setSuperclass(AbstractGwtSpecificValidator.class.getCanonicalName()
+        + "<" + beanType.getQualifiedSourceName() + ">");
+
+    composerFactory.addImplementedInterface(validatorType.getName());
+  }
+
+  @Override
+  protected void writeClassBody(SourceWriter sw) {
+    writeFields(sw);
+    sw.println();
+    writeValidate(sw);
+    sw.println();
+    writeValidateProperty(sw);
+    sw.println();
+    writeValidateValue(sw);
+    sw.println();
+    writeGetDescriptor(sw);
+  }
+
+  protected void writeNewViolations(SourceWriter sw) {
+    // Set<ConstraintViolation<T>> violations = new
+    // HashSet<ConstraintViolation<T>>();
+    sw.println("Set<ConstraintViolation<T>> violations = new HashSet<ConstraintViolation<T>>();");
+  }
+
+  /**
+   * @param sourceWriter
+   */
+  private void writeFields(SourceWriter sw) {
+    // MyBeanDescriptor beanDescriptor = GWT.create(MyBeanDescriptor);
+    sw.println(GwtBeanDescriptor.class.getCanonicalName());
+    sw.println(" beanDescriptor = null; // GWT.create");
+  }
+
+  private void writeGetDescriptor(SourceWriter sw) {
+    // public GwtBeanDescriptor<beanType> getConstraints() {
+    sw.print("public ");
+    sw.print("GwtBeanDescriptor<" + beanHelper.getTypeCanonicalName() + "> ");
+    sw.println("getConstraints() {");
+    sw.indent();
+
+    //    return beanDescriptor;
+    sw.println("return beanDescriptor;");
+
+    sw.outdent();
+    sw.println("}");
+  }
+
+  private void writeValidate(SourceWriter sw) {
+    // public <T> Set<ConstraintViolation<T>> validate(
+    sw.println("public <T> Set<ConstraintViolation<T>> validate(");
+
+    // GwtValidationContext<T> context, BeanType object, Class<?>... groups) {
+    sw.indent();
+    sw.indent();
+    sw.println("GwtValidationContext<T> context,");
+    sw.println(beanHelper.getTypeCanonicalName() + " object,");
+    sw.println("Class<?>... groups) {");
+    sw.outdent();
+
+    writeNewViolations(sw);
+
+    // TODO(nchalko) loop over all constraints
+
+    // return violations;
+    sw.println("return violations;");
+
+    sw.outdent();
+    sw.println("}");
+  }
+
+  private void writeValidateProperty(SourceWriter sw) {
+    // public <T> Set<ConstraintViolation<T>> validate(
+    sw.println("public <T> Set<ConstraintViolation<T>> validateProperty(");
+
+    // GwtValidationContext<T> context, BeanType object, String propertyName,
+    // Class<?>... groups) {
+    sw.indent();
+    sw.indent();
+    sw.println("GwtValidationContext<T> context,");
+    sw.println(beanHelper.getTypeCanonicalName() + " object,");
+    sw.println("String propertyName,");
+    sw.println("Class<?>... groups) {");
+    sw.outdent();
+
+    writeNewViolations(sw);
+
+    // TODO(nchalko) case statement for propertyName
+
+    // return violations;
+    sw.println("return violations;");
+
+    sw.outdent();
+    sw.println("}");
+  }
+
+  private void writeValidateValue(SourceWriter sw) {
+    // public <T> Set<ConstraintViolation<T>> validate(
+    sw.println("public <T> Set<ConstraintViolation<T>> validateValue(");
+
+    // GwtValidationContext<T> context, Class<Author> beanType,
+    // String propertyName, Object value, 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("Class<?>... groups) {");
+    sw.outdent();
+
+    writeNewViolations(sw);
+
+    // TODO(nchalko) case statement for propertyName
+
+    // return violations;
+    sw.println("return violations;");
+
+    sw.outdent();
+    sw.println("}");
+  }
+}
diff --git a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java
new file mode 100644
index 0000000..71cee0f
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorGenerator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.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.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.validation.client.impl.GwtSpecificValidator;
+
+/**
+ * Generates a {@link com.google.gwt.validation.client.GwtSpecificValidator}.
+ * <p>
+ * This class is thread safe.
+ */
+public class GwtSpecificValidatorGenerator extends Generator {
+
+  @Override
+  public String generate(TreeLogger logger, GeneratorContext context,
+      String typeName) throws UnableToCompleteException {
+    JClassType validatorType = context.getTypeOracle().findType(typeName);
+    TypeOracle typeOracle = context.getTypeOracle();
+    assert (typeOracle != null);
+
+    JClassType validator = typeOracle.findType(typeName);
+    if (validator == null) {
+      logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+          + typeName + "'", null);
+      throw new UnableToCompleteException();
+    }
+
+    JClassType gwtSpecificInterface = getGwtSpecificValidator(logger, validator);
+    JClassType beanType = getBeanType(logger, validator, gwtSpecificInterface);
+
+    BeanHelper beanHelper = ValidatorCreator.getBeanHelper(beanType);
+
+    if (beanHelper == null) {
+      logger.log(TreeLogger.ERROR, "Unable to find BeanHelper for " + beanType
+          + " " + GwtSpecificValidator.class.getSimpleName()
+          + " should only be referenced from a class created by "
+          + ValidatorGenerator.class.getCanonicalName(), null);
+      throw new UnableToCompleteException();
+    }
+
+    AbstractCreator creator = new GwtSpecificValidatorCreator(validatorType,
+        beanType, beanHelper, logger, context);
+    return creator.create();
+  }
+
+  private JClassType getBeanType(TreeLogger logger, JClassType validator,
+      JClassType gwtSpecificInterface) throws UnableToCompleteException {
+    if (gwtSpecificInterface instanceof JParameterizedType) {
+      JParameterizedType paramType = (JParameterizedType) gwtSpecificInterface;
+      return paramType.getTypeArgs()[0];
+    }
+    logger.log(TreeLogger.ERROR, validator.getQualifiedSourceName()
+        + " must implement " + GwtSpecificValidator.class.getCanonicalName()
+        + " with a one generic parameter.", null);
+    throw new UnableToCompleteException();
+  }
+
+  private JClassType getGwtSpecificValidator(TreeLogger logger,
+      JClassType validator) throws UnableToCompleteException {
+    for (JClassType interfaceType : validator.getImplementedInterfaces()) {
+      if (interfaceType.getQualifiedSourceName().endsWith(
+          GwtSpecificValidator.class.getCanonicalName())) {
+        return interfaceType;
+      }
+    }
+    logger.log(TreeLogger.ERROR, validator.getQualifiedSourceName()
+        + " must implement " + GwtSpecificValidator.class.getCanonicalName(),
+        null);
+    throw new UnableToCompleteException();
+  }
+}
diff --git a/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
new file mode 100644
index 0000000..e70ea59
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
@@ -0,0 +1,258 @@
+/*
+ * 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.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JPackage;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.validation.client.GwtValidation;
+import com.google.gwt.validation.client.impl.AbstractGwtValidator;
+import com.google.gwt.validation.client.impl.GwtBeanDescriptor;
+import com.google.gwt.validation.client.impl.GwtSpecificValidator;
+import com.google.gwt.validation.client.impl.GwtValidationContext;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.metadata.BeanDescriptor;
+
+/**
+ * Class that creates the validator for the given input class.
+ */
+public class ValidatorCreator {
+
+  // stash the map in a ThreadLocal, since each GWT module lives in its own
+  // thread in DevMode
+  private static final ThreadLocal<Map<JClassType, BeanHelper>> threadLocalHelperMap = new ThreadLocal<Map<JClassType, BeanHelper>>() {
+    protected synchronized Map<JClassType, BeanHelper> initialValue() {
+      return new HashMap<JClassType, BeanHelper>();
+    }
+  };
+
+  public static BeanHelper getBeanHelper(JClassType beanType) {
+    return getBeanHelpers().get(beanType);
+  }
+
+  public static Map<JClassType, BeanHelper> getBeanHelpers() {
+    return Collections.unmodifiableMap(threadLocalHelperMap.get());
+  }
+
+  private final Map<JClassType, BeanHelper> beansToValidate = new HashMap<JClassType, BeanHelper>();
+  private final GeneratorContext context;
+  private final TreeLogger logger;
+  private final JClassType validatorType;
+
+  public ValidatorCreator(JClassType validatorType,
+      GwtValidation gwtValidation, TreeLogger logger,
+      GeneratorContext context) {
+    this.validatorType = validatorType;
+    this.logger = logger;
+    this.context = context;
+    TypeOracle oracle = context.getTypeOracle();
+
+    for (Class<?> clazz : gwtValidation.value()) {
+      JClassType jClass = oracle.findType(clazz.getCanonicalName());
+      BeanHelper helper = new BeanHelper(jClass);
+      beansToValidate.put(jClass, helper);
+    }
+    threadLocalHelperMap.get().putAll(beansToValidate);
+  }
+
+  public String create() {
+    SourceWriter sourceWriter = getSourceWriter(logger, context);
+    if (sourceWriter != null) {
+      writeTypeSupport(sourceWriter);
+      writeValidate(sourceWriter);
+      writeValidateProperty(sourceWriter);
+      writeValidateValue(sourceWriter);
+      writeGetConstraintsForClass(sourceWriter);
+
+      sourceWriter.commit(logger);
+    }
+    return getQaulifiedName();
+  }
+
+  protected void writeContext(SourceWriter sourceWriter, BeanHelper bean,
+      String objectName) {
+    sourceWriter.print(GwtValidationContext.class.getCanonicalName()
+        + "<T> context = new " + GwtValidationContext.class.getCanonicalName()
+        + "<T>(" + objectName + ",");
+    sourceWriter.println(bean.getValidatorInstanceName()
+        + ".getConstraints());");
+  }
+
+  protected void writeGetConstraintsForClass(SourceWriter sourceWriter) {
+    sourceWriter.println("public BeanDescriptor getConstraintsForClass(Class<?> clazz) {");
+    sourceWriter.indent();
+    sourceWriter.println("return null;");
+    sourceWriter.outdent();
+    sourceWriter.println("}");
+    sourceWriter.println();
+  }
+
+  protected void writeIfEqulsBeanType(SourceWriter sourceWriter, BeanHelper bean) {
+    sourceWriter.println("if (object.getClass().equals("
+        + bean.getTypeCanonicalName() + ".class)) {");
+  }
+
+  protected void writeThrowIllegalArgumnet(SourceWriter sourceWriter) {
+    sourceWriter.print("throw new IllegalArgumentException(\""
+        + this.validatorType.getName() + " can only validate ");
+    sourceWriter.print(beansToValidate.toString());
+    sourceWriter.println("\");");
+  }
+
+  protected void writeTypeSupport(SourceWriter sw) {
+    // TODO (nchalko) write these as top level interfaces.
+    // As top level interfaces other generated Validators can use them.
+    // Without it a gwt application can only have ONE validator.
+    for (BeanHelper bean : beansToValidate.values()) {
+      sw.println("public interface " + bean.getValidatorName()
+          + " extends GwtSpecificValidator<" + bean.getTypeCanonicalName()
+          + "> {");
+      sw.println("}");
+
+      sw.println("public interface " + bean.getDescriptorName()
+          + " extends GwtBeanDescriptor<"
+          + bean.getTypeCanonicalName() + "> {");
+      sw.println("}");
+
+      sw.print("private final " + bean.getValidatorName() + " ");
+      sw.print(bean.getValidatorInstanceName());
+      sw.print(" = GWT.create(" + bean.getValidatorName() + ".class);");
+      sw.println();
+    }
+  }
+
+  protected void writeValidate(SourceWriter sw) {
+    sw.println("public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {");
+    sw.indent();
+    for (BeanHelper bean : beansToValidate.values()) {
+      writeValidate(sw, bean);
+    }
+    writeThrowIllegalArgumnet(sw);
+    sw.outdent(); // class
+    sw.println("}");
+    sw.println();
+  }
+
+  protected void writeValidate(SourceWriter sw, BeanHelper bean) {
+    writeIfEqulsBeanType(sw, bean);
+    sw.indent();
+    writeContext(sw, bean, "object");
+    sw.print("return " + bean.getValidatorInstanceName()
+        + ".validate(context, (" + bean.getTypeCanonicalName() + ") object, ");
+    sw.println("groups);");
+    sw.outdent(); // if
+    sw.println("}");
+  }
+
+  protected void writeValidateProperty(SourceWriter sw) {
+    sw.println("public <T> Set<ConstraintViolation<T>> validateProperty(T object,String propertyName, Class<?>... groups) {");
+    sw.indent();
+    for (BeanHelper bean : beansToValidate.values()) {
+      writeValidateProperty(sw, bean);
+    }
+    writeThrowIllegalArgumnet(sw);
+    sw.outdent();
+    sw.println("}");
+    sw.println();
+  }
+
+  protected void writeValidateProperty(SourceWriter sw, BeanHelper bean) {
+    writeIfEqulsBeanType(sw, bean);
+    sw.indent();
+    writeContext(sw, bean, "object");
+    sw.print("return " + bean.getValidatorInstanceName()
+        + ".validateProperty(context, (" + bean.getTypeCanonicalName()
+        + ") object, propertyName, ");
+    sw.println("groups);");
+    sw.outdent(); // if
+    sw.println("}");
+  }
+
+  protected void writeValidateValue(SourceWriter sw) {
+    sw.println("public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {");
+    sw.indent();
+    for (BeanHelper bean : beansToValidate.values()) {
+      writeValidateValue(sw, bean);
+    }
+    writeThrowIllegalArgumnet(sw);
+    sw.outdent();
+    sw.println("}");
+    sw.println();
+  }
+
+  protected void writeValidateValue(SourceWriter sw, BeanHelper bean) {
+    sw.println("if (beanType.getClass().equals(" + bean.getTypeCanonicalName()
+        + ".class)) {");
+    sw.indent();
+    writeContext(sw, bean, "null");
+    sw.println("return " + bean.getValidatorInstanceName()
+        + ".validateValue(context, (Class<" + bean.getTypeCanonicalName()
+        + ">)beanType, propertyName, value, groups);");
+    sw.outdent(); // if
+    sw.println("}");
+  }
+
+  private String getQaulifiedName() {
+    return validatorType.getQualifiedSourceName() + "Impl";
+  }
+
+  private String getSimpleName() {
+    return validatorType.getSimpleSourceName() + "Impl";
+  }
+
+  private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx) {
+    JPackage serviceIntfPkg = validatorType.getPackage();
+    String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
+    String simpleName = getSimpleName();
+    PrintWriter printWriter = ctx.tryCreate(logger, packageName, simpleName);
+    if (printWriter == null) {
+      return null;
+    }
+
+    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
+        packageName, simpleName);
+
+    String[] imports = new String[]{
+        GWT.class.getCanonicalName(),
+        GwtBeanDescriptor.class.getCanonicalName(),
+        GwtSpecificValidator.class.getCanonicalName(),
+        GwtValidationContext.class.getCanonicalName(),
+        Set.class.getCanonicalName(),
+        ConstraintViolation.class.getCanonicalName(),
+        BeanDescriptor.class.getCanonicalName()};
+    for (String imp : imports) {
+      composerFactory.addImport(imp);
+    }
+
+    composerFactory.setSuperclass(AbstractGwtValidator.class.getCanonicalName());
+    SourceWriter sourceWriter = composerFactory.createSourceWriter(ctx,
+        printWriter);
+
+    return sourceWriter;
+  }
+}
diff --git a/user/src/com/google/gwt/validation/rebind/ValidatorGenerator.java b/user/src/com/google/gwt/validation/rebind/ValidatorGenerator.java
new file mode 100644
index 0000000..d64290b
--- /dev/null
+++ b/user/src/com/google/gwt/validation/rebind/ValidatorGenerator.java
@@ -0,0 +1,72 @@
+/*
+ * 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.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.TypeOracle;
+import com.google.gwt.validation.client.GwtValidation;
+
+/**
+ * Generates the generic {@link javax.validation.Validator}. The generic
+ * validator only handles the classes listed in the
+ * {@link com.google.gwt.validation.client.GwtValidation} annotaiton. See
+ * {@link com.google.gwt.validation.client.GwtValidation} for usage.
+ */
+public class ValidatorGenerator extends Generator {
+
+  @Override
+  public String generate(TreeLogger logger, GeneratorContext context,
+      String typeName) throws UnableToCompleteException {
+    JClassType validatorType = context.getTypeOracle().findType(typeName);
+    TypeOracle typeOracle = context.getTypeOracle();
+    assert (typeOracle != null);
+
+    JClassType validator = typeOracle.findType(typeName);
+    if (validator == null) {
+      logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+          + typeName + "'", null);
+      throw new UnableToCompleteException();
+    }
+
+    GwtValidation gwtValidation = validatorType.findAnnotationInTypeHierarchy(GwtValidation.class);
+
+    if (gwtValidation == null) {
+      logger.log(TreeLogger.ERROR, typeName + "Must be anntotated with "
+          + GwtValidation.class.getCanonicalName(), null);
+      throw new UnableToCompleteException();
+    }
+
+    if (gwtValidation.value().length == 0) {
+      logger.log(TreeLogger.ERROR,
+          "The @" + GwtValidation.class.getSimpleName() + "  of " + typeName
+              + "must specify at least one bean type to validate.", null);
+      throw new UnableToCompleteException();
+    }
+
+    TreeLogger validatorLogger = logger.branch(TreeLogger.DEBUG,
+        "Generating Validator for  '" + validator.getQualifiedSourceName()
+            + "'", null);
+    ValidatorCreator creator = new ValidatorCreator(validatorType,
+        gwtValidation,
+        validatorLogger,
+        context);
+    return creator.create();
+  }
+}
diff --git a/user/test/com/google/gwt/validation/example/client/AuthorTest.java b/user/test/com/google/gwt/validation/example/client/AuthorTest.java
new file mode 100644
index 0000000..2be400a
--- /dev/null
+++ b/user/test/com/google/gwt/validation/example/client/AuthorTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.example.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+
+/**
+ * Tests for {@link Author}.
+ */
+public class AuthorTest extends GWTTestCase {
+
+  private Author author;
+
+  private Validator validator;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.validation.example.ValidationExample";
+  }
+
+  public void testValidate_string() {
+    try {
+      validator.validate("some string");
+      fail("Expected a " + IllegalArgumentException.class);
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testValidate_valid() {
+    author.setFirstName("John");
+    author.setLastName("Smith");
+    author.setCompany("Google");
+    Set<ConstraintViolation<Author>> violations = validator.validate(author);
+    assertContentsAnyOrder("valid author", violations);
+  }
+
+  public void testValidateProperty_object() {
+    try {
+      validator.validateProperty(new Object(), "foo");
+      fail("Expected a " + IllegalArgumentException.class);
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  public void testValidateValue_string() {
+    try {
+      validator.validateValue(String.class, "notValid", "value");
+      fail("Expected a " + IllegalArgumentException.class);
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+  }
+
+  protected Validator createValidator() {
+    return GWT.create(ExampleGwtValidator.class);
+  }
+
+  @Override
+  protected final void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    author = new Author();
+    validator = createValidator();
+  }
+
+  private <T> void assertContentsAnyOrder(String message,
+      Iterable<T> actual, T... expected) {
+
+    List<T> expectedList = Arrays.asList(expected);
+    message += "Expected to find " + expectedList + " but found " + actual;
+    for (T a : actual) {
+      if (expectedList.contains(a)) {
+        expectedList.remove(a);
+      } else {
+        fail(message);
+      }
+    }
+    if (!expectedList.isEmpty()) {
+      fail(message);
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/validation/example/client/ExampleValidationClientGwtSuite.java b/user/test/com/google/gwt/validation/example/client/ExampleValidationClientGwtSuite.java
new file mode 100644
index 0000000..e9a711a
--- /dev/null
+++ b/user/test/com/google/gwt/validation/example/client/ExampleValidationClientGwtSuite.java
@@ -0,0 +1,33 @@
+/*
+ * 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.example.client;
+
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * All Constraints tests that GWTTestCase.
+ */
+public class ExampleValidationClientGwtSuite {
+  public static Test suite() {
+    GWTTestSuite suite = new GWTTestSuite(
+        "Validation Example tests that require GWT");
+    suite.addTestSuite(AuthorTest.class);
+    return suite;
+  }
+
+}