Support paths for iterables without a key or an index.
[JSR 303 TCK Result] 116 of 257 (45.14%) Pass with 14 Failures and 9 Errors.

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

Review by: rchandia@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9837 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java b/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java
index b42b4e7..3573ee9 100644
--- a/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java
+++ b/user/src/com/google/gwt/validation/client/impl/GwtValidationContext.java
@@ -76,7 +76,6 @@
   /**
    * Append a node named name to the path..
    *
-   * @param name
    * @return the new GwtValidationContext.
    */
   public GwtValidationContext<T> append(String name) {
@@ -87,9 +86,8 @@
   }
 
   /**
-   * Append a indexed node to the path.
+   * Append an indexed node to the path.
    *
-   * @param name
    * @return the new GwtValidationContext.
    */
   public GwtValidationContext<T> appendIndex(String name, int index) {
@@ -100,6 +98,18 @@
   }
 
   /**
+   * Append an iterable node to the path.
+   *
+   * @return the new GwtValidationContext.
+   */
+  public GwtValidationContext<T> appendIterable(String name) {
+    GwtValidationContext<T> temp = new GwtValidationContext<T>(rootBean,
+        beanDescriptor, messageInterpolator, validator, validatedObjects);
+    temp.path = path.appendIterable(name);
+    return temp;
+  }
+
+  /**
    * Append a keyed node to the path.
    *
    * @return the new GwtValidationContext.
diff --git a/user/src/com/google/gwt/validation/client/impl/NodeImpl.java b/user/src/com/google/gwt/validation/client/impl/NodeImpl.java
index 0482ee0..910585c 100644
--- a/user/src/com/google/gwt/validation/client/impl/NodeImpl.java
+++ b/user/src/com/google/gwt/validation/client/impl/NodeImpl.java
@@ -25,51 +25,36 @@
 class NodeImpl implements Node, Serializable {
 
   private static final long serialVersionUID = 1L;
-  public static final Node ROOT_NODE = new NodeImpl(null);
+  public static final Node ROOT_NODE = new NodeImpl(null, null, null, false);
 
+  static Node createIndexedNode(String name, Integer index) {
+    return new NodeImpl(name, null, index, true);
+  }
+
+  static Node createIterableNode(String name) {
+    return new NodeImpl(name, null, null, true);
+  }
+
+  static Node createKeyedNode(String name, Object key) {
+    return new NodeImpl(name, key, null, true);
+  }
+
+  static Node createNode(String name) {
+    return new NodeImpl(name, null, null, false);
+  }
+
+  private final boolean isInIterable;
   private final String name;
   private final Integer index;
+
   private final Object key;
 
-  /**
-   * Create a non iterable node.
-   *
-   * @param name the possibly <code>null</code> name.
-   */
-  public NodeImpl(String name) {
-    this.name = name;
-    this.index = null;
-    this.key = null;
-  }
 
-  /**
-   * Create an iterable node with an index.
-   *
-   * @param name the possibly <code>null</code> name.
-   * @param index the zero based index.
-   */
-  public NodeImpl(String name, int index) {
-    if (index < 0) {
-      throw new IllegalArgumentException("Index can not be negative.");
-    }
+  private NodeImpl(String name, Object key, Integer index, boolean iterable) {
     this.name = name;
-    this.index = Integer.valueOf(index);
-    this.key = null;
-  }
-
-  /**
-   * Create an iterable node with a key.
-   *
-   * @param name the possibly <code>null</code> name.
-   * @param key the lookup key for this node.
-   */
-  public NodeImpl(String name, Object key) {
-    if (key == null) {
-      throw new NullPointerException();
-    }
-    this.name = name;
-    this.index = null;
     this.key = key;
+    this.index = index;
+    this.isInIterable = iterable;
   }
 
   @Override
@@ -81,9 +66,11 @@
       return false;
     }
     NodeImpl that = (NodeImpl) obj;
-    return (this.name == null ? that.name == null : this.name == that.name)
-        && (this.index == null ? that.index == null : this.index == that.index)
-        && (this.key == null ? that.key == null : this.key == that.key);
+    return (this.name == null ? that.name == null : this.name.equals(that.name))
+        && (this.index == null ? that.index == null : this.index
+            .equals(that.index))
+        && (this.key == null ? that.key == null : this.key.equals(that.key))
+        && this.isInIterable == that.isInIterable;
   }
 
   public Integer getIndex() {
@@ -105,11 +92,12 @@
     result = prime * result + ((index == null) ? 0 : index.hashCode());
     result = prime * result + ((key == null) ? 0 : key.hashCode());
     result = prime * result + ((name == null) ? 0 : name.hashCode());
+    result = prime * result + (isInIterable ? 0 : 1);
     return result;
   }
 
   public boolean isInIterable() {
-    return index != null || key != null;
+    return isInIterable;
   }
 
   @Override
@@ -122,7 +110,7 @@
       sb.append('[');
       if (key != null) {
         sb.append(key);
-      } else {
+      } else if (index != null) {
         sb.append(index);
       }
       sb.append(']');
diff --git a/user/src/com/google/gwt/validation/client/impl/PathImpl.java b/user/src/com/google/gwt/validation/client/impl/PathImpl.java
index f481564..6b20e3a 100644
--- a/user/src/com/google/gwt/validation/client/impl/PathImpl.java
+++ b/user/src/com/google/gwt/validation/client/impl/PathImpl.java
@@ -54,11 +54,11 @@
    * @return The new path with appended node.
    */
   public PathImpl append(String name) {
-    return new PathImpl(this, new NodeImpl(name));
+    return new PathImpl(this, NodeImpl.createNode(name));
   }
 
   /**
-   * Create a new path with a indexed node named <code>name</code> appended to
+   * Create a new path with an indexed node named <code>name</code> appended to
    * the existing path.
    *
    * @param name
@@ -66,7 +66,18 @@
    * @return The new path with appended node.
    */
   public PathImpl appendIndex(String name, int index) {
-    return new PathImpl(this, new NodeImpl(name, index));
+    return new PathImpl(this, NodeImpl.createIndexedNode(name, index));
+  }
+
+  /**
+   * Create a new path with an iterable node named <code>name</code> appended to
+   * the existing path.
+   *
+   * @param name
+   * @return The new path with appended node.
+   */
+  public PathImpl appendIterable(String name) {
+    return new PathImpl(this, NodeImpl.createIterableNode(name));
   }
 
   /**
@@ -78,7 +89,7 @@
    * @return The new path with appended node.
    */
   public PathImpl appendKey(String name, Object key) {
-    return new PathImpl(this, new NodeImpl(name, key));
+    return new PathImpl(this, NodeImpl.createKeyedNode(name, key));
   }
 
   @Override
diff --git a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
index eafec76..ea587d6 100644
--- a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
+++ b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
@@ -1139,10 +1139,18 @@
     sw.indent();
     sw.indent();
 
-    // context.appendIndex("myProperty",i++),
-    sw.print("context.appendIndex(\"");
-    sw.print(p.getPropertyName());
-    sw.println("\",i++),");
+    Class<?> elementClass = p.getElementClass();
+    if (elementClass.isArray() || List.class.isAssignableFrom(elementClass)) {
+      // context.appendIndex("myProperty",i++),
+      sw.print("context.appendIndex(\"");
+      sw.print(p.getPropertyName());
+      sw.println("\",i),");
+    } else {
+      // context.appendIterable("myProperty"),
+      sw.print("context.appendIterable(\"");
+      sw.print(p.getPropertyName());
+      sw.println("\"),");
+    }
 
     // instance, groups));
     sw.println("instance, groups));");
@@ -1155,6 +1163,9 @@
     sw.outdent();
     sw.println("}");
 
+    // i++;
+    sw.println("i++;");
+
     // }
     sw.outdent();
     sw.println("}");
diff --git a/user/test/com/google/gwt/validation/client/impl/NodeImplTest.java b/user/test/com/google/gwt/validation/client/impl/NodeImplTest.java
index ca9b74c..38e4b12 100644
--- a/user/test/com/google/gwt/validation/client/impl/NodeImplTest.java
+++ b/user/test/com/google/gwt/validation/client/impl/NodeImplTest.java
@@ -24,21 +24,25 @@
  */
 public class NodeImplTest extends TestCase {
 
-  public void testRoot() throws Exception {
-    assertNode(NodeImpl.ROOT_NODE, null, false, null, null);
+  public void testFoo() throws Exception {
+    assertNode(NodeImpl.createNode("foo"), "foo", false, null, null);
   }
 
-  public void testFoo() throws Exception {
-    assertNode(new NodeImpl("foo"), "foo", false, null, null);
+  public void testFoo_iterable() throws Exception {
+    assertNode(NodeImpl.createIterableNode("foo"), "foo", true, null, null);
   }
 
   public void testFoo1() throws Exception {
-    assertNode(new NodeImpl("foo", 1), "foo", true, null,
+    assertNode(NodeImpl.createIndexedNode("foo", 1), "foo", true, null,
         Integer.valueOf(1));
   }
 
   public void testFooBar() throws Exception {
-    assertNode(new NodeImpl("foo", "bar"), "foo", true, "bar", null);
+    assertNode(NodeImpl.createKeyedNode("foo", "bar"), "foo", true, "bar", null);
+  }
+
+  public void testRoot() throws Exception {
+    assertNode(NodeImpl.ROOT_NODE, null, false, null, null);
   }
 
   protected void assertNode(Node node, String expectedName,
diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java
index 9c9692a..5fee2ac 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/ConstraintCompositionGwtTest.java
@@ -43,7 +43,6 @@
     delegate.testComposedConstraintsAreRecursive();
   }
 
-  @Failing(issue = 5799)
   public void testEachFailingConstraintCreatesConstraintViolation() {
     delegate.testEachFailingConstraintCreatesConstraintViolation();
   }
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java
index 3bd146b..93c8ddf 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/PropertyPathGwtTest.java
@@ -32,9 +32,7 @@
     delegate.testPropertyPathTraversedObject();
   }
 
-  @Failing(issue = 5982)
   public void testPropertyPathWithArray() {
-    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testPropertyPathWithArray();
   }
 
@@ -42,9 +40,7 @@
     delegate.testPropertyPathWithConstraintViolationForRootObject();
   }
 
-  @Failing(issue = 5982)
   public void testPropertyPathWithList() {
-    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testPropertyPathWithList();
   }
 
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java b/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java
index 9ad1acc..2d129f7 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/ValidateGwtTest.java
@@ -34,15 +34,13 @@
     delegate.testConstraintViolation();
   }
 
-  @Failing(issue = 5982)
+  @Failing(issue = 5930)
   public void testGraphValidationWithArray() {
-    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testGraphValidationWithArray();
   }
 
-  @Failing(issue = 5982)
+  @Failing(issue = 5930)
   public void testGraphValidationWithList() {
-    fail("Force an early failure for Issue 5982 to prevent all following tests from failing.");
     delegate.testGraphValidationWithList();
   }
 
@@ -103,7 +101,6 @@
     }
   }
 
-  @Failing(issue = 5930)
   public void testValidationIsPolymorphic() {
     delegate.testValidationIsPolymorphic();
   }