Fix for bad association of errors generated by client-side JSR-303 when using nested, validated beans.

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10886 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/editor/client/impl/SimpleViolation.java b/user/src/com/google/gwt/editor/client/impl/SimpleViolation.java
index 1293477..8835503 100644
--- a/user/src/com/google/gwt/editor/client/impl/SimpleViolation.java
+++ b/user/src/com/google/gwt/editor/client/impl/SimpleViolation.java
@@ -75,7 +75,7 @@
 
     @Override
     public Object getKey() {
-      return v.getLeafBean();
+      return v.getRootBean();
     }
 
     @Override
diff --git a/user/test/com/google/gwt/validation/example/ExampleValidationClientGwtSuite.java b/user/test/com/google/gwt/validation/example/ExampleValidationClientGwtSuite.java
index c13ee78..8ec3640 100644
--- a/user/test/com/google/gwt/validation/example/ExampleValidationClientGwtSuite.java
+++ b/user/test/com/google/gwt/validation/example/ExampleValidationClientGwtSuite.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.gwt.validation.example.client.AuthorTest;
+import com.google.gwt.validation.example.client.BookTest;
 
 import junit.framework.Test;
 
@@ -28,6 +29,7 @@
     GWTTestSuite suite = new GWTTestSuite(
         "Validation Example tests that require GWT");
     suite.addTestSuite(AuthorTest.class);
+    suite.addTestSuite(BookTest.class);
     return suite;
   }
 
diff --git a/user/test/com/google/gwt/validation/example/ValidationExample.gwt.xml b/user/test/com/google/gwt/validation/example/ValidationExample.gwt.xml
index a719210..fe7f629 100644
--- a/user/test/com/google/gwt/validation/example/ValidationExample.gwt.xml
+++ b/user/test/com/google/gwt/validation/example/ValidationExample.gwt.xml
@@ -16,6 +16,7 @@
   the License.
 -->
 <module>
+  <inherits name="com.google.gwt.editor.Editor" />
   <inherits name="com.google.gwt.user.User" />
   <inherits name="org.hibernate.validator.HibernateValidator" />
   <source path="client">
@@ -24,4 +25,4 @@
     class="com.google.gwt.validation.example.client.ExampleValidatorFactory">
     <when-type-is class="javax.validation.ValidatorFactory" />
 </replace-with>
-</module>
\ No newline at end of file
+</module>
diff --git a/user/test/com/google/gwt/validation/example/client/BookTest.java b/user/test/com/google/gwt/validation/example/client/BookTest.java
new file mode 100644
index 0000000..f954f00
--- /dev/null
+++ b/user/test/com/google/gwt/validation/example/client/BookTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012 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.editor.client.Editor;
+import com.google.gwt.editor.client.EditorError;
+import com.google.gwt.editor.client.SimpleBeanEditorDriver;
+import com.google.gwt.editor.ui.client.ValueBoxEditorDecorator;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.TextBox;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+
+/**
+ * Tests for {@link Book}.
+ */
+public class BookTest extends GWTTestCase {
+
+  private Author author;
+  private Book book;
+  private BookWidget bookWidget;
+  private Driver driver;
+
+  private Validator validator;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.validation.example.ValidationExample";
+  }
+
+  public void testValidate_emptyAuthorLastName() {
+    initValidBook();
+    author.setLastName("");
+    Set<ConstraintViolation<Book>> violations = validator.validate(book);
+    assertEquals(1, violations.size());
+    ConstraintViolation<Book> violation = violations.iterator().next();
+    
+    assertEquals(author, violation.getLeafBean());
+    assertEquals(book, violation.getRootBean());
+    assertEquals("author.lastName", violation.getPropertyPath().toString());
+  }
+
+  public void testValidate_valid() {
+    initValidBook();
+    Set<ConstraintViolation<Book>> violations = validator.validate(book);
+    assertTrue(violations.isEmpty());
+  }
+  
+  public void testErrorDisplay_noErrors() {
+    initValidBook();
+    driver.initialize(bookWidget);
+    driver.edit(book);
+    assertEquals("Smith", bookWidget.author.lastName.asEditor().getValue());
+    
+    Set<ConstraintViolation<Book>> violations = validator.validate(book);
+    assertTrue(violations.isEmpty());
+    
+    driver.setConstraintViolations(doHorribleCast(violations));
+  }
+  
+  public void testErrorDisplay_lastNameError() {
+    initValidBook();
+    driver.initialize(bookWidget);
+    driver.edit(book);
+    
+    bookWidget.author.lastName.asEditor().setValue("");
+    driver.flush();
+    assertEquals(null, author.getLastName());
+    
+    Set<ConstraintViolation<Book>> violations = validator.validate(book);
+    assertEquals(1, violations.size());
+    
+    driver.setConstraintViolations(doHorribleCast(violations));
+    List<EditorError> errors = bookWidget.author.errors;
+    // Note: Is the fact that there are 2 errors here (rather than just one) also a bug?
+    assertEquals(2, errors.size());
+    
+    EditorError error = errors.get(0);
+    assertEquals(bookWidget.author.lastName.asEditor(), error.getEditor());
+    // There is a bug here - error.getPath() throws a StringIndexOutOfBoundsException.
+    // The pathPrefixLength is set in ErrorCollector line 63. It breaks the toString() method
+    // of SimpleError as well.
+    assertEquals("author.lastName", error.getAbsolutePath());
+  }
+
+  @Override
+  protected final void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    author = new Author();
+    book = new Book();
+    validator = Validation.buildDefaultValidatorFactory().getValidator();
+    bookWidget = new BookWidget();
+    driver = GWT.create(Driver.class);
+  }
+
+  protected void initValidBook() {
+    author.setFirstName("John");
+    author.setLastName("Smith");
+    author.setCompany("Google");
+    book.setAuthor(author);
+    book.setTitle("JSR-303 Validation in GWT");
+  }
+  
+  @SuppressWarnings("unchecked")
+  private Iterable<ConstraintViolation<?>> doHorribleCast(Object o) {
+    return (Iterable<ConstraintViolation<?>>) o;
+  }
+  
+  interface Driver extends SimpleBeanEditorDriver<Book, BookWidget> { }
+  
+  class BookWidget extends Composite implements Editor<Book> {
+    AuthorWidget author = new AuthorWidget();
+  }
+  
+  class AuthorWidget extends Composite implements Editor<Author> {
+    ValueBoxEditorDecorator<String> lastName;
+
+    List<EditorError> errors;
+    
+    AuthorWidget() {
+      lastName = new ValueBoxEditorDecorator<String>() {
+        @Override
+        public void showErrors(List<EditorError> errors) {
+          super.showErrors(errors);
+          AuthorWidget.this.errors = errors;
+        }
+      };
+      lastName.setValueBox(new TextBox());
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/validation/example/client/ExampleValidatorFactory.java b/user/test/com/google/gwt/validation/example/client/ExampleValidatorFactory.java
index 05f1a70..4aae839 100644
--- a/user/test/com/google/gwt/validation/example/client/ExampleValidatorFactory.java
+++ b/user/test/com/google/gwt/validation/example/client/ExampleValidatorFactory.java
@@ -41,7 +41,7 @@
    * client.
    */
   @GwtValidation(
-      value = {Author.class},
+      value = {Author.class, Book.class},
       groups = {Default.class, ClientGroup.class})
   public interface GwtValidator extends Validator {
   }