Public: custom serializer and other code needed to send constraint violations
from the server to the client.
Review at http://gwt-code-reviews.appspot.com/1040802
Review by: bobv@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9135 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/common.ant.xml b/samples/common.ant.xml
index aa2939f..73468b9 100755
--- a/samples/common.ant.xml
+++ b/samples/common.ant.xml
@@ -71,7 +71,8 @@
<target name="compile" description="Compile all java files">
<mkdir dir="${sample.build}/war/WEB-INF/classes" />
- <gwt.javac destdir="${sample.build}/war/WEB-INF/classes">
+ <gwt.javac destdir="${sample.build}/war/WEB-INF/classes"
+ excludes="**/super/**">
<classpath>
<pathelement location="${gwt.user.jar}" />
<pathelement location="${gwt.dev.jar}" />
diff --git a/samples/validation/build.xml b/samples/validation/build.xml
index 5f960a7..03f7a24 100755
--- a/samples/validation/build.xml
+++ b/samples/validation/build.xml
@@ -24,5 +24,10 @@
<include name="apache/log4j/log4j-1.2.16.jar" />
<include name="slf4j/slf4j-api/slf4j-api-1.6.1.jar" />
<include name="slf4j/slf4j-log4j12/slf4j-log4j12-1.6.1.jar" />
+ <!-- Needed for JDK 1.5-->
+ <include name="javax/activation/activation-1.1.jar" />
+ <include name="javax/xml/bind/jaxb-api-2.1.jar" />
+ <include name="sun/jaxb/jaxb-impl-2.1.3.jar" />
+ <include name="javax/xml/stream/stax-api-1.0-2.jar" />
</fileset>
</project>
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 55dc76a..84de2f3 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
@@ -22,4 +22,5 @@
<source path='client'/>
<source path='shared'/>
+ <super-source path="super" />
</module>
diff --git a/samples/validation/src/com/google/gwt/sample/validation/client/GreetingService.java b/samples/validation/src/com/google/gwt/sample/validation/client/GreetingService.java
index c54f455..9ed0787 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/client/GreetingService.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/client/GreetingService.java
@@ -16,13 +16,17 @@
package com.google.gwt.sample.validation.client;
import com.google.gwt.rpc.client.RpcService;
+import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.sample.validation.shared.Person;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+import javax.validation.ConstraintViolationException;
+
/**
* The client side stub for the RPC service.
*/
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RpcService {
- String greetServer(Person name) throws IllegalArgumentException;
+ SafeHtml greetServer(Person name) throws IllegalArgumentException,
+ ConstraintViolationException;
}
diff --git a/samples/validation/src/com/google/gwt/sample/validation/client/GreetingServiceAsync.java b/samples/validation/src/com/google/gwt/sample/validation/client/GreetingServiceAsync.java
index dda64fb..1309e56 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/client/GreetingServiceAsync.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/client/GreetingServiceAsync.java
@@ -15,13 +15,16 @@
*/
package com.google.gwt.sample.validation.client;
+import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.sample.validation.shared.Person;
import com.google.gwt.user.client.rpc.AsyncCallback;
+import javax.validation.ConstraintViolationException;
+
/**
* The async counterpart of <code>GreetingService</code>.
*/
public interface GreetingServiceAsync {
- void greetServer(Person person, AsyncCallback<String> callback)
- throws IllegalArgumentException;
+ void greetServer(Person person, AsyncCallback<SafeHtml> callback)
+ throws IllegalArgumentException, ConstraintViolationException;
}
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
index 5f8c74c..dffe96a 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/client/SampleValidator.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/client/SampleValidator.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.sample.validation.client;
+import com.google.gwt.sample.validation.shared.ClientGroup;
import com.google.gwt.sample.validation.shared.Person;
import com.google.gwt.validation.client.GwtValidation;
@@ -27,17 +28,6 @@
*/
@GwtValidation(value = Person.class,
groups = {
- Default.class, SampleValidator.ClientGroup.class})
+ Default.class, ClientGroup.class})
public interface SampleValidator extends Validator {
- /**
- * Client Validation Group
- */
- public interface ClientGroup {
- }
-
- /**
- * Server Validation Group
- */
- public interface ServerGroup {
- }
}
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 21a90f7..1383761 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
@@ -20,6 +20,7 @@
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.sample.validation.shared.Person;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
@@ -36,6 +37,7 @@
import java.util.Set;
import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
/**
@@ -124,7 +126,6 @@
person.setName(nameField.getText());
Validator validator = GWT.create(SampleValidator.class);
-
Set<ConstraintViolation<Person>> violations = validator.validate(person);
if (!violations.isEmpty()) {
StringBuffer errorMessage = new StringBuffer();
@@ -140,8 +141,24 @@
sendButton.setEnabled(false);
textToServer.setText(person.getName());
serverResponse.setText("");
- greetingService.greetServer(person, new AsyncCallback<String>() {
+ greetingService.greetServer(person, new AsyncCallback<SafeHtml>() {
public void onFailure(Throwable caught) {
+ if (caught instanceof ConstraintViolationException) {
+ ConstraintViolationException violationException = (ConstraintViolationException) caught;
+ Set<ConstraintViolation<?>> violations = violationException.getConstraintViolations();
+ StringBuffer sb = new StringBuffer();
+ for (ConstraintViolation<?> constraintViolation : violations) {
+ sb.append(constraintViolation.getPropertyPath().toString()) //
+ .append(":") //
+ .append(constraintViolation.getMessage()) //
+ .append("\n");
+ }
+ errorLabel.setText(sb.toString());
+ sendButton.setEnabled(true);
+ sendButton.setFocus(true);
+ return;
+ }
+
// Show the RPC error message to the user
dialogBox.setText("Remote Procedure Call - Failure");
serverResponse.addStyleName(style.error());
@@ -150,7 +167,7 @@
closeButton.setFocus(true);
}
- public void onSuccess(String result) {
+ public void onSuccess(SafeHtml result) {
dialogBox.setText("Remote Procedure Call");
serverResponse.removeStyleName(style.error());
serverResponse.setHTML(result);
diff --git a/samples/validation/src/com/google/gwt/sample/validation/server/GreetingServiceImpl.java b/samples/validation/src/com/google/gwt/sample/validation/server/GreetingServiceImpl.java
index 3ee9de7..f8712c7 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/server/GreetingServiceImpl.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/server/GreetingServiceImpl.java
@@ -16,8 +16,20 @@
package com.google.gwt.sample.validation.server;
import com.google.gwt.rpc.server.RpcServlet;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.sample.validation.client.GreetingService;
import com.google.gwt.sample.validation.shared.Person;
+import com.google.gwt.sample.validation.shared.ServerGroup;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.groups.Default;
/**
* The server side implementation of the RPC service.
@@ -26,35 +38,33 @@
public class GreetingServiceImpl extends RpcServlet implements
GreetingService {
- public String greetServer(Person person) throws IllegalArgumentException {
- // Verify that the input is valid.
+ private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
- // TODO(nchalko) validate
+ public SafeHtml greetServer(Person person) throws IllegalArgumentException,
+ ConstraintViolationException {
+ // Verify that the input is valid.
+ Set<ConstraintViolation<Person>> violations = validator.validate(person,
+ Default.class, ServerGroup.class);
+ if (!violations.isEmpty()) {
+ Set<ConstraintViolation<?>> temp = new HashSet<ConstraintViolation<?>>(
+ violations);
+ throw new ConstraintViolationException(temp);
+ }
String serverInfo = getServletContext().getServerInfo();
String userAgent = getThreadLocalRequest().getHeader("User-Agent");
// Escape data from the client to avoid cross-site script vulnerabilities.
- String value = escapeHtml(person.getName());
- userAgent = escapeHtml(userAgent);
+ SafeHtmlBuilder builder = new SafeHtmlBuilder();
- return "Hello, " + value + "!<br><br>I am running " + serverInfo
- + ".<br><br>It looks like you are using:<br>" + userAgent;
+ SafeHtml safeHtml = builder//
+ .appendEscapedLines("Hello, " + person.getName() + "!")//
+ .appendHtmlConstant("<br>")//
+ .appendEscaped("I am running " + serverInfo + ".")//
+ .appendHtmlConstant("<br><br>")//
+ .appendEscaped("It looks like you are using: ")//
+ .appendEscaped(userAgent)//
+ .toSafeHtml();
+ return safeHtml;
}
-
- /**
- * Escape an html string. Escaping data received from the client helps to
- * prevent cross-site script vulnerabilities.
- *
- * @param html the html string to escape
- * @return the escaped string
- */
- private String escapeHtml(String html) {
- // TODO(nchalko) use SafeHtml after it's integrated.
- if (html == null) {
- return null;
- }
- return html.replaceAll("&", "&").replaceAll("<", "<").replaceAll(
- ">", ">");
- }
-}
+}
\ No newline at end of file
diff --git a/samples/validation/src/com/google/gwt/sample/validation/shared/ClientGroup.java b/samples/validation/src/com/google/gwt/sample/validation/shared/ClientGroup.java
new file mode 100644
index 0000000..0acd314
--- /dev/null
+++ b/samples/validation/src/com/google/gwt/sample/validation/shared/ClientGroup.java
@@ -0,0 +1,22 @@
+/*
+ * 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.shared;
+
+/**
+ * Client Validation Group
+ */
+public interface ClientGroup {
+}
\ No newline at end of file
diff --git a/samples/validation/src/com/google/gwt/sample/validation/shared/NoOp.java b/samples/validation/src/com/google/gwt/sample/validation/shared/NoOp.java
index 197a655..1459303 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/shared/NoOp.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/shared/NoOp.java
@@ -31,7 +31,7 @@
import javax.validation.Payload;
/**
- * Test constaint that is always valid
+ * Test constraint that is always valid
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE})
@Retention(RUNTIME)
diff --git a/samples/validation/src/com/google/gwt/sample/validation/shared/Person.java b/samples/validation/src/com/google/gwt/sample/validation/shared/Person.java
index 82a68eb..5bb73db 100644
--- a/samples/validation/src/com/google/gwt/sample/validation/shared/Person.java
+++ b/samples/validation/src/com/google/gwt/sample/validation/shared/Person.java
@@ -23,7 +23,7 @@
/**
* A sample bean to show validation on.
*/
-@NoOp
+@ServerConstraint(groups = ServerGroup.class)
public class Person implements IsSerializable {
@NotNull
diff --git a/samples/validation/src/com/google/gwt/sample/validation/shared/ServerConstraint.java b/samples/validation/src/com/google/gwt/sample/validation/shared/ServerConstraint.java
new file mode 100644
index 0000000..290062b
--- /dev/null
+++ b/samples/validation/src/com/google/gwt/sample/validation/shared/ServerConstraint.java
@@ -0,0 +1,47 @@
+/*
+ * 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.shared;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * Sample constraint that is designed to only run on the server. It will fail if
+ * the Persons name is "Fail"
+ */
+@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE})
+@Retention(RUNTIME)
+@Documented
+@Constraint(validatedBy = {ServerValidator.class})
+public @interface ServerConstraint {
+ String message() default "Not valid at the server";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+}
\ No newline at end of file
diff --git a/samples/validation/src/com/google/gwt/sample/validation/shared/ServerGroup.java b/samples/validation/src/com/google/gwt/sample/validation/shared/ServerGroup.java
new file mode 100644
index 0000000..bc28da9
--- /dev/null
+++ b/samples/validation/src/com/google/gwt/sample/validation/shared/ServerGroup.java
@@ -0,0 +1,22 @@
+/*
+ * 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.shared;
+
+/**
+ * Server Validation Group
+ */
+public interface ServerGroup {
+}
\ No newline at end of file
diff --git a/samples/validation/src/com/google/gwt/sample/validation/shared/ServerValidator.java b/samples/validation/src/com/google/gwt/sample/validation/shared/ServerValidator.java
new file mode 100644
index 0000000..9e15123
--- /dev/null
+++ b/samples/validation/src/com/google/gwt/sample/validation/shared/ServerValidator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.shared;
+
+import java.lang.reflect.Method;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Fails only on the server if the persons name is "Fail"
+ */
+public class ServerValidator implements
+ ConstraintValidator<ServerConstraint, Person> {
+
+ public void initialize(ServerConstraint constraintAnnotation) {
+ // Here I do something that will not compile on GWT
+ Method[] methods = constraintAnnotation.getClass().getMethods();
+ }
+
+ public boolean isValid(Person person, ConstraintValidatorContext context) {
+ if (person == null) {
+ return true;
+ }
+ String name = person.getName();
+ return name == null || !name.equals("Fail");
+ }
+}
\ No newline at end of file
diff --git a/samples/validation/src/com/google/gwt/sample/validation/super/com/google/gwt/sample/validation/shared/ServerValidator.java b/samples/validation/src/com/google/gwt/sample/validation/super/com/google/gwt/sample/validation/shared/ServerValidator.java
new file mode 100644
index 0000000..330d8ad
--- /dev/null
+++ b/samples/validation/src/com/google/gwt/sample/validation/super/com/google/gwt/sample/validation/shared/ServerValidator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.shared;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Always passes.
+ * <p>
+ * TODO(nchalko) change this to extend
+ * {@link com.google.gwt.validation.client.constraints.NotGwtCompatibleValidator}
+ * when groups are properly handled.
+ */
+public class ServerValidator implements
+ ConstraintValidator<ServerConstraint, Person> {
+
+ public void initialize(ServerConstraint constraintAnnotation) {
+ }
+
+ public boolean isValid(Person person, ConstraintValidatorContext context) {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/user/build.xml b/user/build.xml
index 60319ea..6c40ce0 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -146,7 +146,7 @@
<exclude name="javax/validation/super/javax/validation/MessageInterpolator.java"/>
<exclude name="javax/validation/super/javax/validation/Configuration.java"/>
<exclude name="javax/validation/super/javax/validation/Validation.java"/>
- <exclude name="org/hibernate/validator/super/org/hibernate/validator/constraints/ScriptAssert.java"/>
+ <exclude name="org/hibernate/validator/super/org/hibernate/validator/**/*.java"/>
</fileset>
<fileset dir="super/com/google/gwt/emul" />
<fileset dir="super/com/google/gwt/junit/translatable" />
diff --git a/user/src/javax/validation/ConstraintViolationException_CustomFieldSerializer.java b/user/src/javax/validation/ConstraintViolationException_CustomFieldSerializer.java
new file mode 100644
index 0000000..c73aef0
--- /dev/null
+++ b/user/src/javax/validation/ConstraintViolationException_CustomFieldSerializer.java
@@ -0,0 +1,48 @@
+/*
+ * 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 javax.validation;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+import java.util.Set;
+
+/**
+ * Custom Serializer for {@link ConstraintViolationException}.
+ */
+public class ConstraintViolationException_CustomFieldSerializer {
+
+ public static void deserialize(SerializationStreamReader streamReader,
+ ConstraintViolationException instance) throws SerializationException {
+ // no fields
+ }
+
+ public static ConstraintViolationException instantiate(
+ SerializationStreamReader streamReader)
+ throws SerializationException {
+ String message = streamReader.readString();
+ @SuppressWarnings("unchecked")
+ Set<ConstraintViolation<?>> set = (Set<ConstraintViolation<?>>) streamReader.readObject();
+ return new ConstraintViolationException(message, set);
+ }
+
+ public static void serialize(SerializationStreamWriter streamWriter,
+ ConstraintViolationException instance) throws SerializationException {
+ streamWriter.writeString(instance.getMessage());
+ streamWriter.writeObject(instance.getConstraintViolations());
+ }
+}
diff --git a/user/src/org/hibernate/validator/HibernateValidator.gwt.xml b/user/src/org/hibernate/validator/HibernateValidator.gwt.xml
index b469c70..626fd9c 100644
--- a/user/src/org/hibernate/validator/HibernateValidator.gwt.xml
+++ b/user/src/org/hibernate/validator/HibernateValidator.gwt.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<module>
-<!--
-Import this module to use Hibernate Validator during the compilation of validation classes for
+<!--
+Import this module to use Hibernate Validator during the compilation of validation classes for
gwt clients.
-->
<inherits name='com.google.gwt.validation.Validation' />
@@ -10,7 +10,9 @@
<exclude name="super/" />
</source>
<source path="engine">
+ <include name="ConstraintViolationImpl.java"/>
<include name="NodeImpl.java"/>
+ <include name="PathImpl.java"/>
</source>
<super-source path="super" />
-</module>
\ No newline at end of file
+</module>
diff --git a/user/src/org/hibernate/validator/engine/ConstraintViolationImpl_CustomFieldSerializer.java b/user/src/org/hibernate/validator/engine/ConstraintViolationImpl_CustomFieldSerializer.java
new file mode 100644
index 0000000..97883fa
--- /dev/null
+++ b/user/src/org/hibernate/validator/engine/ConstraintViolationImpl_CustomFieldSerializer.java
@@ -0,0 +1,81 @@
+/*
+ * 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 org.hibernate.validator.engine;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+import java.lang.annotation.ElementType;
+
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+
+/**
+ * Custom Serializer for {@link ConstraintViolationImpl}.
+ */
+public class ConstraintViolationImpl_CustomFieldSerializer {
+
+ public static void deserialize(SerializationStreamReader streamReader,
+ ConstraintViolationImpl instance) throws SerializationException {
+ // no fields
+ }
+
+ public static ConstraintViolationImpl<Object> instantiate(
+ SerializationStreamReader streamReader) throws SerializationException {
+
+ String messageTemplate = null;
+ String interpolatedMessage = streamReader.readString();
+ Class<Object> rootBeanClass = null;
+ Object rootBean = null;
+ Object leafBeanInstance = null;
+ Object value = null;
+ Path propertyPath = (Path) streamReader.readObject();
+ ConstraintDescriptor<?> constraintDescriptor = null;
+ ElementType elementType = null;
+ return new ConstraintViolationImpl<Object>(messageTemplate,
+ interpolatedMessage, rootBeanClass, rootBean, leafBeanInstance, value,
+ propertyPath, constraintDescriptor, elementType);
+ }
+
+ /**
+ * Only a subset of fields are actually serialized.
+ * <p/>
+ * There is no guarantee that the root bean is GWT-serializable or that it's
+ * appropriate for it to be exposed on the client. Even if the root bean could
+ * be sent back, the lack of reflection on the client makes it troublesome to
+ * interpret the path as a sequence of property accesses.
+ * <p/>
+ * The current implementation is the simplest-to-implement properties.
+ * <ol>
+ * <li>Message</li>
+ * <li>Property Path</li>
+ * </ol>
+ */
+ public static void serialize(SerializationStreamWriter streamWriter,
+ ConstraintViolationImpl instance) throws SerializationException {
+
+ // streamWriter.writeString(instance.getMessageTemplate());
+ streamWriter.writeString(instance.getMessage());
+ // streamWriter.writeObject(instance.getRootBeanClass());
+ // streamWriter.writeObject(instance.getRootBean());
+ // streamWriter.writeObject(instance.getLeafBean());
+ // streamWriter.writeObject(instance.getInvalidValue());
+ streamWriter.writeObject(instance.getPropertyPath());
+ // streamWriter.writeObject(instance.getConstraintDescriptor());
+ // ElementType
+ }
+}
diff --git a/user/src/org/hibernate/validator/engine/PathImpl_CustomFieldSerializer.java b/user/src/org/hibernate/validator/engine/PathImpl_CustomFieldSerializer.java
new file mode 100644
index 0000000..67c6c59
--- /dev/null
+++ b/user/src/org/hibernate/validator/engine/PathImpl_CustomFieldSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.hibernate.validator.engine;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Custom Serializer for {@link PathImpl}.
+ */
+public class PathImpl_CustomFieldSerializer {
+
+ public static void deserialize(SerializationStreamReader streamReader,
+ PathImpl instance) throws SerializationException {
+ // no fields
+ }
+
+ public static PathImpl instantiate(SerializationStreamReader streamReader)
+ throws SerializationException {
+ String propertyPath = streamReader.readString();
+
+ return PathImpl.createPathFromString(propertyPath);
+ }
+
+ public static void serialize(SerializationStreamWriter streamWriter,
+ PathImpl instance) throws SerializationException {
+ streamWriter.writeString(instance.toString());
+ }
+}
diff --git a/user/src/org/hibernate/validator/super/org/hibernate/validator/engine/PathImpl.java b/user/src/org/hibernate/validator/super/org/hibernate/validator/engine/PathImpl.java
new file mode 100644
index 0000000..91bd2c3
--- /dev/null
+++ b/user/src/org/hibernate/validator/super/org/hibernate/validator/engine/PathImpl.java
@@ -0,0 +1,225 @@
+// $Id: PathImpl.java 17744 2009-10-14 14:38:57Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.
+*/
+// Modified by Google: Replace java.util.Pattern with gwt RegExp
+package org.hibernate.validator.engine;
+
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.validation.Path;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class PathImpl implements Path, Serializable {
+
+ private static final long serialVersionUID = 7564511574909882392L;
+
+ /**
+ * Regular expression used to split a string path into its elements.
+ *
+ * @see <a href="http://www.regexplanet.com/simple/index.jsp">Regular expression tester</a>
+ */
+ private static final RegExp pathPattern = RegExp.compile("(\\w+)(\\[(\\w*)\\])?(\\.(.*))*");
+
+ private static final String PROPERTY_PATH_SEPERATOR = ".";
+
+ private final List<Node> nodeList;
+
+ /**
+ * Returns a {@code Path} instance representing the path described by the given string. To create a root node the empty string should be passed.
+ *
+ * @param propertyPath the path as string representation.
+ *
+ * @return a {@code Path} instance representing the path described by the given string.
+ *
+ * @throws IllegalArgumentException in case {@code property == null} or {@code property} cannot be parsed.
+ */
+ public static PathImpl createPathFromString(String propertyPath) {
+ if ( propertyPath == null ) {
+ throw new IllegalArgumentException( "null is not allowed as property path." );
+ }
+
+ if ( propertyPath.length() == 0 ) {
+ return createNewPath( null );
+ }
+
+ return parseProperty( propertyPath );
+ }
+
+ public static PathImpl createNewPath(String name) {
+ PathImpl path = new PathImpl();
+ NodeImpl node = new NodeImpl( name );
+ path.addNode( node );
+ return path;
+ }
+
+ public static PathImpl createShallowCopy(Path path) {
+ return path == null ? null : new PathImpl( path );
+ }
+
+ private PathImpl(Path path) {
+ this.nodeList = new ArrayList<Node>();
+ for ( Object aPath : path ) {
+ nodeList.add( new NodeImpl( ( Node ) aPath ) );
+ }
+ }
+
+ private PathImpl() {
+ nodeList = new ArrayList<Node>();
+ }
+
+ private PathImpl(List<Node> nodeList) {
+ this.nodeList = new ArrayList<Node>();
+ for ( Node node : nodeList ) {
+ this.nodeList.add( new NodeImpl( node ) );
+ }
+ }
+
+ public boolean isRootPath() {
+ return nodeList.size() == 1 && nodeList.get( 0 ).getName() == null;
+ }
+
+ public PathImpl getPathWithoutLeafNode() {
+ List<Node> nodes = new ArrayList<Node>( nodeList );
+ PathImpl path = null;
+ if ( nodes.size() > 1 ) {
+ nodes.remove( nodes.size() - 1 );
+ path = new PathImpl( nodes );
+ }
+ return path;
+ }
+
+ public void addNode(Node node) {
+ nodeList.add( node );
+ }
+
+ public Node removeLeafNode() {
+ if ( nodeList.size() == 0 ) {
+ throw new IllegalStateException( "No nodes in path!" );
+ }
+ if ( nodeList.size() == 1 ) {
+ throw new IllegalStateException( "Root node cannot be removed!" );
+ }
+ return nodeList.remove( nodeList.size() - 1 );
+ }
+
+ public NodeImpl getLeafNode() {
+ if ( nodeList.size() == 0 ) {
+ throw new IllegalStateException( "No nodes in path!" );
+ }
+ return ( NodeImpl ) nodeList.get( nodeList.size() - 1 );
+ }
+
+ public Iterator<Path.Node> iterator() {
+ return nodeList.iterator();
+ }
+
+ public boolean isSubPathOf(Path path) {
+ Iterator<Node> pathIter = path.iterator();
+ Iterator<Node> thisIter = iterator();
+ while ( pathIter.hasNext() ) {
+ Node pathNode = pathIter.next();
+ if ( !thisIter.hasNext() ) {
+ return false;
+ }
+ Node thisNode = thisIter.next();
+ if ( !thisNode.equals( pathNode ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ Iterator<Path.Node> iter = iterator();
+ while ( iter.hasNext() ) {
+ Node node = iter.next();
+ builder.append( node.toString() );
+ if ( iter.hasNext() ) {
+ builder.append( PROPERTY_PATH_SEPERATOR );
+ }
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ PathImpl path = ( PathImpl ) o;
+ if ( nodeList != null && !nodeList.equals( path.nodeList ) ) {
+ return false;
+ }
+ if ( nodeList == null && path.nodeList != null ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return nodeList != null ? nodeList.hashCode() : 0;
+ }
+
+ private static PathImpl parseProperty(String property) {
+ PathImpl path = new PathImpl();
+ String tmp = property;
+ do {
+ MatchResult matcher = pathPattern.exec(tmp);
+ if (matcher != null) {
+ String value = matcher.getGroup(1);
+ String indexed = matcher.getGroup(2);
+ String index = matcher.getGroup(3);
+ NodeImpl node = new NodeImpl( value );
+ if ( indexed != null ) {
+ node.setInIterable( true );
+ }
+ if ( index != null && index.length() > 0 ) {
+ try {
+ Integer i = Integer.parseInt( index );
+ node.setIndex( i );
+ }
+ catch ( NumberFormatException e ) {
+ node.setKey( index );
+ }
+ }
+ path.addNode( node );
+ tmp = matcher.getGroup(5);
+ }
+ else {
+ throw new IllegalArgumentException( "Unable to parse property path " + property );
+ }
+ } while ( tmp != null );
+ return path;
+ }
+
+}