Sort the top level validator class to handle most specific classes first.

[JSR 303 TCK Result] 87 of 257 (33.85%) Pass with 39 Failures and 14 Errors.

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

Review by: rchandia@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9708 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/validation/rebind/Util.java b/user/src/com/google/gwt/validation/rebind/Util.java
index b0616ab..0d140fc 100644
--- a/user/src/com/google/gwt/validation/rebind/Util.java
+++ b/user/src/com/google/gwt/validation/rebind/Util.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
@@ -22,10 +22,10 @@
 import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
 import com.google.gwt.thirdparty.guava.common.collect.Iterables;
 import com.google.gwt.thirdparty.guava.common.collect.Lists;
-import com.google.gwt.thirdparty.guava.common.collect.Ordering;
 import com.google.gwt.thirdparty.guava.common.collect.Sets;
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -37,14 +37,14 @@
   /**
    * Creates a Predicate that returns false if source contains an associated
    * class that is a super type of the class associated with the tested T.
-   * 
+   *
    * @param <T> the type to test
-   * @param source the Set of <T> to look for class matches.
+   * @param source the set of <T> to look for class matches.
    * @param toClass Function from T to Class
    * @return newly create predicate.
    */
-  static <T> Predicate<T> createMostSpecificMatchPredicate(final Set<T> source,
-      final Function<T, Class<?>> toClass) {
+  static <T> Predicate<T> createMostSpecificMatchPredicate(
+      final Iterable<T> source, final Function<T, Class<?>> toClass) {
     return new Predicate<T>() {
 
       public boolean apply(T input) {
@@ -62,7 +62,7 @@
   /**
    * Selects first only the classes that are assignable from the target, and
    * then returns the most specific matching classes.
-   * 
+   *
    * @param target the Class to match
    * @param availableClasses classes to search
    * @return Set of only the most specific classes that match the target.
@@ -91,16 +91,24 @@
    */
   static <T> ImmutableList<T> sortMostSpecificFirst(Iterable<T> classes,
       Function<T, Class<?>> toClass) {
-    Set<T> working = Sets.newHashSet(classes);
+    List<T> working = Lists.newArrayList();
+    // strip duplicates
+    for (T t : classes) {
+      if (!working.contains(t)) {
+        working.add(t);
+      }
+    }
     List<T> sorted = Lists.newArrayList();
-    Predicate<T> mostSpecific = createMostSpecificMatchPredicate(working, toClass);
+    Predicate<T> mostSpecific = createMostSpecificMatchPredicate(working,
+        toClass);
     boolean changed = false;
     do {
       changed = false;
-      for (T t : Ordering.usingToString().sortedCopy(working)) {
+      for (Iterator<T> iterator = working.iterator(); iterator.hasNext();) {
+        T t = iterator.next();
         if (mostSpecific.apply(t)) {
           sorted.add(t);
-          working.remove(t);
+          iterator.remove();
           changed = true;
         }
       }
diff --git a/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
index 33e75fe..ff00dee 100644
--- a/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
+++ b/user/src/com/google/gwt/validation/rebind/ValidatorCreator.java
@@ -20,6 +20,7 @@
 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.thirdparty.guava.common.collect.ImmutableList;
 import com.google.gwt.thirdparty.guava.common.collect.Lists;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
@@ -43,10 +44,9 @@
   /**
    * The beans to validate in source declaration order.
    */
-  private final List<BeanHelper> beansToValidate = Lists.newArrayList();
+  private final ImmutableList<BeanHelper> beansToValidate;
   private final GwtValidation gwtValidation;
 
-
   public ValidatorCreator(JClassType validatorType, //
       GwtValidation gwtValidation, //
       TreeLogger logger, //
@@ -54,11 +54,12 @@
     super(context, logger, validatorType);
     this.gwtValidation = gwtValidation;
 
-
+    List<BeanHelper> temp = Lists.newArrayList();
     for (Class<?> clazz : gwtValidation.value()) {
       BeanHelper helper = createBeanHelper(clazz);
-      beansToValidate.add(helper);
+      temp.add(helper);
     }
+    beansToValidate = Util.sortMostSpecificFirst(temp, BeanHelper.TO_CLAZZ);
   }
 
   @Override
diff --git a/user/test/com/google/gwt/validation/rebind/UtilTest.java b/user/test/com/google/gwt/validation/rebind/UtilTest.java
index f55f07c..1e42976 100644
--- a/user/test/com/google/gwt/validation/rebind/UtilTest.java
+++ b/user/test/com/google/gwt/validation/rebind/UtilTest.java
@@ -57,46 +57,56 @@
 
   private static void assertContentsInOrder(List<Class<?>> actual,
       Class<?>... classes) {
-    assertEquals(ImmutableList.copyOf(classes), actual);
+    assertEquals(ImmutableList.copyOf(classes), ImmutableList.copyOf(actual));
   }
 
-  private static ImmutableSet<Class<?>> of(Class<?>... classes) {
+  private static ImmutableList<Class<?>> list(Class<?>... classes) {
+    return ImmutableList.copyOf(classes);
+  }
+
+  private static ImmutableSet<Class<?>> set(Class<?>... classes) {
     return ImmutableSet.copyOf(classes);
   }
 
   public void testBestMatches_Bobby2() {
     Set<Class<?>> actual = findBestMatches(Bobby2.class,
-        of(Alice.class, Bob.class, Bobby.class));
+        set(Alice.class, Bob.class, Bobby.class));
     assertEquals(1, actual.size());
     assertEquals(Bobby.class, Iterables.get(actual, 0));
   }
 
   public void testBestMatches_none() {
-    Set<Class<?>> actual = Util.findBestMatches(Bob.class, of(Alice.class));
+    Set<Class<?>> actual = Util.findBestMatches(Bob.class, set(Alice.class));
     assertEquals(0, actual.size());
   }
 
   public void testBestMatches_one() {
     Set<Class<?>> actual = findBestMatches(Bob.class,
-        of(Alice.class, Bob.class));
+        set(Alice.class, Bob.class));
     assertEquals(1, actual.size());
     assertEquals(Bob.class, Iterables.get(actual, 0));
   }
 
   public void testBestMatches_two() {
-    Set<Class<?>> actual = findBestMatches(Chuck.class, of(C1.class, C2.class));
+    Set<Class<?>> actual = findBestMatches(Chuck.class, set(C1.class, C2.class));
     assertEquals(2, actual.size());
   }
 
   public void testSortMostSpecificFirst_chuck() {
 
     List<Class<?>> actual = Util.sortMostSpecificFirst(
-        of(C2.class, C1.class, Chuck.class), classIdentity);
-    assertContentsInOrder(actual, Chuck.class, C1.class, C2.class);
+        list(C2.class, C1.class, Chuck.class), classIdentity);
+    assertContentsInOrder(actual, Chuck.class, C2.class, C1.class);
+  }
+
+  public void testSortMostSpecificFirst_double() {
+    List<Class<?>> actual = Util.sortMostSpecificFirst(
+        list(Alice.class, Alice.class, Bob.class), classIdentity);
+    assertContentsInOrder(actual, Alice.class, Bob.class);
   }
 
   public void testSortMostSpecificFirst_one() {
-    List<Class<?>> actual = Util.sortMostSpecificFirst(of(Alice.class),
+    List<Class<?>> actual = Util.sortMostSpecificFirst(list(Alice.class),
         classIdentity);
     assertContentsInOrder(actual, Alice.class);
   }
diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/TckTestValidatorFactory.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/TckTestValidatorFactory.java
index a1db13b..38aaa79 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/TckTestValidatorFactory.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/constraintcomposition/TckTestValidatorFactory.java
@@ -31,8 +31,7 @@
    * Marker Interface for {@link GWT#create(Class)}.
    */
   @GwtValidation(value = {
-      // German and French must be listed before the Address Super class
-      GermanAddress.class, FrenchAddress.class, Address.class, Friend.class,
+      Address.class, FrenchAddress.class, Friend.class, GermanAddress.class,
       Shoe.class
       // TODO(nchalko) handle ConstraintDefinitionException
       // ConstraintCompositionGwtTest.DummyEntityWithZipCode.class
diff --git a/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/TckTestValidatorFactory.java b/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/TckTestValidatorFactory.java
index 2475214..85e7c8b 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/TckTestValidatorFactory.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/constraints/validatorresolution/TckTestValidatorFactory.java
@@ -36,7 +36,7 @@
    * Marker Interface for {@link GWT#create(Class)}.
    */
   @GwtValidation(value = {
-      Bar.class, CustomInterfaceImpl.class, CustomClass.class, Foo.class,
+      Bar.class, CustomClass.class, CustomInterfaceImpl.class, Foo.class,
       MinMax.class, SubClassAHolder.class, SubClassBHolder.class, Suburb.class})
   public static interface GwtValidator extends Validator {
   }
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/TckTestValidatorFactory.java b/user/test/org/hibernate/jsr303/tck/tests/validation/TckTestValidatorFactory.java
index f3e0055..12cb1b7 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/TckTestValidatorFactory.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/TckTestValidatorFactory.java
@@ -35,10 +35,9 @@
    * Marker Interface for {@link GWT#create(Class)}.
    */
   @GwtValidation(value = {
-      // Actor must be after its subclasses
-      ActorDB.class, ActorArrayBased.class, ActorListBased.class, Actor.class,
-      Address.class, BadlyBehavedEntity.class, Car.class,
-      Customer.class, Engine.class, Order.class, VerySpecialClass.class})
+      ActorArrayBased.class, ActorDB.class, ActorListBased.class, Actor.class,
+      Address.class, BadlyBehavedEntity.class, Car.class, Customer.class,
+      Engine.class, Order.class, VerySpecialClass.class})
   public static interface GwtValidator extends Validator {
   }
 
diff --git a/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java b/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java
index 1f6e2ca..4ce7357 100644
--- a/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java
+++ b/user/test/org/hibernate/jsr303/tck/tests/validation/graphnavigation/TckTestValidatorFactory.java
@@ -31,10 +31,9 @@
    * Marker Interface for {@link GWT#create(Class)}.
    */
   @GwtValidation(value = {
-      AnimalCaretaker.class, Elephant.class, Condor.class, GameReserve.class,
-      Parent.class,
-      MultiCage.class, MultiCage.class, SingleCage.class, Zebra.class,
-      Zoo.class})
+      AnimalCaretaker.class, Condor.class, Elephant.class, GameReserve.class,
+      MultiCage.class, MultiCage.class, Parent.class, SingleCage.class,
+      Zebra.class, Zoo.class})
   public static interface GwtValidator extends Validator {
   }