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;
+ }
+
+}