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/972801 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9120 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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; + } + +}