Cherry picking r9536 releases/[rel_#] for issue 4781


git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.2@9537 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java b/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
index 4cabead..dd09b6e 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
@@ -28,6 +28,7 @@
 import com.google.gwt.uibinder.rebind.MortalLogger;
 import com.google.gwt.uibinder.rebind.UiBinderContext;
 
+import java.beans.Introspector;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -44,8 +45,8 @@
  */
 public class OwnerFieldClass {
   
-  private static final int defaultCost = 4;
-  private static final Map<String, Integer> typeRank;
+  private static final int DEFAULT_COST = 4;
+  private static final Map<String, Integer> TYPE_RANK;
   static {
     HashMap<String, Integer> tmpTypeRank = new HashMap<String, Integer>();
     tmpTypeRank.put("java.lang.String", 1);
@@ -65,7 +66,7 @@
     tmpTypeRank.put("java.lang.Integer", 3);
     tmpTypeRank.put("java.lang.Long", 3);
     tmpTypeRank.put("java.lang.Short", 3);
-    typeRank = Collections.unmodifiableMap(tmpTypeRank);
+    TYPE_RANK = Collections.unmodifiableMap(tmpTypeRank);
   }
   
   /**
@@ -158,6 +159,24 @@
   }
 
   /**
+   * Adds a setter for a given property to the given map of setters.
+   *
+   * @param allSetters the map of setters (keyed by property name)
+   * @param propertyName the property name to use
+   * @param method the setter to use
+   */
+  private void addSetter(Map<String, Collection<JMethod>> allSetters,
+      String propertyName, JMethod method) {
+    Collection<JMethod> propertyMethods = allSetters.get(propertyName);
+    if (propertyMethods == null) {
+      propertyMethods = new ArrayList<JMethod>();
+      allSetters.put(propertyName, propertyMethods);
+    }
+
+    propertyMethods.add(method);
+  }
+
+  /**
    * Given a collection of setters for the same property, picks which one to
    * use. Not having a proper setter is not an error unless of course the user
    * tries to use it.
@@ -230,16 +249,15 @@
       String propertyName = method.getName().substring(3);
 
       // turn "PropertyName" into "propertyName"
-      propertyName = propertyName.substring(0, 1).toLowerCase()
+      String beanPropertyName = Introspector.decapitalize(propertyName);
+      addSetter(allSetters, beanPropertyName, method);
+
+      // keep backwards compatibility (i.e. hTML instead of HTML for setHTML)
+      String legacyPropertyName = propertyName.substring(0, 1).toLowerCase()
           + propertyName.substring(1);
-
-      Collection<JMethod> propertyMethods = allSetters.get(propertyName);
-      if (propertyMethods == null) {
-        propertyMethods = new ArrayList<JMethod>();
-        allSetters.put(propertyName, propertyMethods);
+      if (!legacyPropertyName.equals(beanPropertyName)) {
+        addSetter(allSetters, legacyPropertyName, method);
       }
-
-      propertyMethods.add(method);
     }
 
     return allSetters;
@@ -349,7 +367,7 @@
    * types, it will not be considered ambiguous. 
    *  
    * The cost mapping is defined in 
-   * {@link #typeRank typeRank }
+   * {@link #TYPE_RANK typeRank }
    * @param method.
    * @return the rank of the method.
    */
@@ -358,9 +376,9 @@
     int rank = 0;
     for (int i = 0; i < Math.min(params.length, 10); i++) {
       JType paramType = params[i].getType();
-      int cost = defaultCost;
-      if (typeRank.containsKey(paramType.getQualifiedSourceName())) {
-        cost = typeRank.get(paramType.getQualifiedSourceName());
+      int cost = DEFAULT_COST;
+      if (TYPE_RANK.containsKey(paramType.getQualifiedSourceName())) {
+        cost = TYPE_RANK.get(paramType.getQualifiedSourceName());
       }
       assert (cost >= 0 && cost <= 0x07);
       rank = rank | (cost << (3 * i));
diff --git a/user/test/com/google/gwt/uibinder/rebind/model/OwnerFieldClassTest.java b/user/test/com/google/gwt/uibinder/rebind/model/OwnerFieldClassTest.java
index 9c84e79..c34fc7a 100644
--- a/user/test/com/google/gwt/uibinder/rebind/model/OwnerFieldClassTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/model/OwnerFieldClassTest.java
@@ -29,7 +29,7 @@
 import com.google.gwt.uibinder.rebind.MortalLogger;
 import com.google.gwt.uibinder.rebind.UiBinderContext;
 import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.HTML;
 
 import junit.framework.TestCase;
 
@@ -51,23 +51,35 @@
   }
 
   public void testOwnerFieldClass() throws Exception {
-    // Get the JType for a Label
-    JClassType labelType = gwtTypeAdapter.adaptJavaClass(Label.class);
+    // Get the JType for an HTML
+    JClassType htmlType = gwtTypeAdapter.adaptJavaClass(HTML.class);
 
     // Now get its field class model
-    OwnerFieldClass fieldClass = OwnerFieldClass.getFieldClass(labelType,
+    OwnerFieldClass fieldClass = OwnerFieldClass.getFieldClass(htmlType,
         MortalLogger.NULL, uiBinderCtx);
 
     // Check the class model properties
-    assertEquals(labelType, fieldClass.getRawType());
+    assertEquals(htmlType, fieldClass.getRawType());
     assertNull(fieldClass.getUiConstructor());
 
-    JMethod setter = fieldClass.getSetter("visible");
-    assertMethod(setter, "setVisible", JPrimitiveType.BOOLEAN);
+    // simple property: visible="" maps to setVisible
+    JMethod visibleSetter = fieldClass.getSetter("visible");
+    assertMethod(visibleSetter, "setVisible", JPrimitiveType.BOOLEAN);
+
+    // all-upper-case property, Java Bean naming (all-upper-cased)
+    JMethod htmlSetter = fieldClass.getSetter("HTML");
+    assertMethod(htmlSetter, "setHTML",
+        gwtTypeAdapter.adaptJavaClass(String.class));
+
+    // all-upper-case property, GWT-legacy naming with the first char
+    // lower-cased
+    htmlSetter = fieldClass.getSetter("hTML");
+    assertMethod(htmlSetter, "setHTML",
+        gwtTypeAdapter.adaptJavaClass(String.class));
 
     // Check that the same instance of the model is returned if asked again
-    assertSame(fieldClass, OwnerFieldClass.getFieldClass(labelType,
-        MortalLogger.NULL, uiBinderCtx));
+    assertSame(fieldClass,
+        OwnerFieldClass.getFieldClass(htmlType, MortalLogger.NULL, uiBinderCtx));
 
     gwtTypeAdapter.verifyAll();
   }
@@ -153,47 +165,47 @@
     }
     
     // setvalue1 is not ambiguous
-    public void setValue1(boolean b) {
+    public void setValue1(@SuppressWarnings("unused") boolean b) {
     }
 
-    public void setValue1(Boolean b) {
+    public void setValue1(@SuppressWarnings("unused") Boolean b) {
     }
 
     // derived wins
-    public void setValue2(Integer b) {
+    public void setValue2(@SuppressWarnings("unused") Integer b) {
     }
 
     // this overload wins
-    public void setValue3(int b) {
+    public void setValue3(@SuppressWarnings("unused") int b) {
     }
     
     // this is not ambiguous since derived
     // has the exact same signature
-    public void setValue4(int b) {
+    public void setValue4(@SuppressWarnings("unused") int b) {
     }
     
     // setvalue5 is ambiguous
-    public void setValue5(float f) {
+    public void setValue5(@SuppressWarnings("unused") float f) {
     }
     
-    public void setValue5(double d) {
+    public void setValue5(@SuppressWarnings("unused") double d) {
     }
     
     // string always wins
-    public void setValue6(String s) {
+    public void setValue6(@SuppressWarnings("unused") String s) {
     }
 
-    public void setValue6(char s) {
+    public void setValue6(@SuppressWarnings("unused") char s) {
     }
     
-    public void setValue6(Object s) {
+    public void setValue6(@SuppressWarnings("unused") Object s) {
     }
     
     // primitive wins
-    public void setValue7(int s) {
+    public void setValue7(@SuppressWarnings("unused") int s) {
     }
     
-    public void setValue7(StringBuffer s) {
+    public void setValue7(@SuppressWarnings("unused") StringBuffer s) {
     }
   }
 
@@ -207,10 +219,10 @@
       super();
     }
     
-    public void setValue2(int b) {
+    public void setValue2(@SuppressWarnings("unused") int b) {
     }
     
-    public void setValue3(Integer b) {
+    public void setValue3(@SuppressWarnings("unused") Integer b) {
     }
     
     public void setValue4(int b) {
@@ -498,8 +510,7 @@
   public void testOwnerFieldClass_withBadlyNamedMethod() {
     JClassType parentType = gwtTypeAdapter.adaptJavaClass(UiChildWithPoorMethodNames.class);
     try {
-      OwnerFieldClass parentClass = OwnerFieldClass.getFieldClass(parentType,
-          MortalLogger.NULL, uiBinderCtx);
+      OwnerFieldClass.getFieldClass(parentType, MortalLogger.NULL, uiBinderCtx);
       fail("Class should error because @UiChild method has invalid name (and no tag specified).");
     } catch (UnableToCompleteException expected) {
       gwtTypeAdapter.verifyAll();
@@ -553,7 +564,7 @@
     }
   }
 
-  public void testOwnerFieldClass_withMultipleUiConstructors() throws Exception {
+  public void testOwnerFieldClass_withMultipleUiConstructors() {
     JClassType constructorsType = gwtTypeAdapter.adaptJavaClass(MultiUiConstructorsClass.class);
 
     try {