Support is/has methods in Editor framework.
Issue 6040.
Patch by: bobv
Review by: rjrjr

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10288 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/editor/rebind/model/EditorModel.java b/user/src/com/google/gwt/editor/rebind/model/EditorModel.java
index 378cb4f..c17a709 100644
--- a/user/src/com/google/gwt/editor/rebind/model/EditorModel.java
+++ b/user/src/com/google/gwt/editor/rebind/model/EditorModel.java
@@ -22,13 +22,13 @@
 import com.google.gwt.core.ext.typeinfo.JGenericType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
-import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.editor.client.CompositeEditor;
 import com.google.gwt.editor.client.Editor;
 import com.google.gwt.editor.client.IsEditor;
 import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -376,13 +376,6 @@
     return toReturn.toArray(new EditorData[toReturn.size()]);
   }
 
-  private String camelCase(String prefix, String name) {
-    StringBuilder sb = new StringBuilder();
-    sb.append(prefix).append(Character.toUpperCase(name.charAt(0))).append(
-        name, 1, name.length());
-    return sb.toString();
-  }
-
   private List<EditorData> createEditorData(EditorAccess access)
       throws UnableToCompleteException {
     TreeLogger subLogger = logger.branch(TreeLogger.DEBUG, "Examining "
@@ -471,64 +464,73 @@
         continue;
       }
       boolean lastPart = i == j - 1;
-      String getterName = camelCase("get", parts[i]);
+      boolean foundGetterForPart = false;
 
-      for (JClassType search : lookingAt.getFlattenedSupertypeHierarchy()) {
-        // If looking at the last element of the path, also look for a setter
-        if (i == j - 1 && setterName == null) {
-          for (JMethod maybeSetter : search.getOverloads(camelCase("set",
-              parts[i]))) {
-            if (maybeSetter.getReturnType().equals(JPrimitiveType.VOID)
-                && maybeSetter.getParameters().length == 1) {
+      for (JMethod maybeSetter : lookingAt.getInheritableMethods()) {
+        JBeanMethod which = JBeanMethod.which(maybeSetter);
+        if (JBeanMethod.CALL.equals(which)) {
+          continue;
+        }
+        if (!which.inferName(maybeSetter).equals(parts[i])) {
+          continue;
+        }
+        switch (which) {
+          case GET: {
+            JType returnType = maybeSetter.getReturnType();
+            lookingAt = returnType.isClassOrInterface();
+            if (!lastPart && lookingAt == null) {
+              poison(foundPrimitiveMessage(returnType, interstitialGetters.toString(), path));
+              return;
+            }
+            interstitialGetters.append(".").append(maybeSetter.getName()).append("()");
+            interstitialGuard.append(" && %1$s").append(interstitialGetters).append(" != null");
+            builder.propertyOwnerType(maybeSetter.getEnclosingType());
+            foundGetterForPart = true;
+            if (!lastPart) {
+              continue part;
+            }
+            break;
+          }
+          case SET:
+          case SET_BUILDER: {
+            if (lastPart && setterName == null) {
+              /*
+               * If looking at the last element of the path, also look for a
+               * setter.
+               */
+
               JType setterParamType = maybeSetter.getParameters()[0].getType();
               // Handle the case of setFoo(int) vs. Editor<Integer>
               if (setterParamType.isPrimitive() != null) {
                 // Replace the int with Integer
-                setterParamType = oracle.findType(setterParamType.isPrimitive().getQualifiedBoxedSourceName());
+                setterParamType =
+                    oracle.findType(setterParamType.isPrimitive().getQualifiedBoxedSourceName());
               }
-              boolean matches = setterParamType.isClassOrInterface().isAssignableFrom(
-                  propertyType);
+              boolean matches = setterParamType.isClassOrInterface().isAssignableFrom(propertyType);
               if (matches) {
                 setterName = maybeSetter.getName();
-                break;
               }
             }
+            break;
           }
         }
-
-        JMethod getter = search.findMethod(getterName, new JType[0]);
-        if (getter != null) {
-          JType returnType = getter.getReturnType();
-          lookingAt = returnType.isClassOrInterface();
-          if (!lastPart && lookingAt == null) {
-            poison(foundPrimitiveMessage(returnType,
-                interstitialGetters.toString(), path));
-            return;
-          }
-          interstitialGetters.append(".").append(getterName).append("()");
-          interstitialGuard.append(" && %1$s").append(interstitialGetters).append(
-              " != null");
-          builder.propertyOwnerType(search);
-          continue part;
-        }
       }
-      poison(noGetterMessage(path, proxyType));
-      return;
+      if (!foundGetterForPart) {
+        poison(noGetterMessage(path, proxyType));
+        return;
+      }
     }
 
     int idx = interstitialGetters.lastIndexOf(".");
-    builder.beanOwnerExpression(idx <= 0 ? "" : interstitialGetters.substring(
-        0, idx));
+    builder.beanOwnerExpression(idx <= 0 ? "" : interstitialGetters.substring(0, idx));
     if (parts.length > 1) {
       // Strip after last && since null is a valid value
-      interstitialGuard.delete(interstitialGuard.lastIndexOf(" &&"),
-          interstitialGuard.length());
+      interstitialGuard.delete(interstitialGuard.lastIndexOf(" &&"), interstitialGuard.length());
       builder.beanOwnerGuard(interstitialGuard.substring(8));
     }
     if (interstitialGetters.length() > 0) {
       builder.getterExpression("."
-          + interstitialGetters.substring(idx + 1,
-              interstitialGetters.length() - 2) + "()");
+          + interstitialGetters.substring(idx + 1, interstitialGetters.length() - 2) + "()");
     } else {
       builder.getterExpression("");
     }
diff --git a/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java b/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
index ba3ae78..bb270d7 100644
--- a/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
+++ b/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
@@ -188,7 +188,7 @@
         types.findType("t.CompositeEditorDriver"), rfedType);
 
     EditorData[] data = m.getEditorData();
-    assertEquals(7, data.length);
+    assertEquals(9, data.length);
 
     String[] paths = new String[data.length];
     String[] expressions = new String[data.length];
@@ -196,20 +196,22 @@
       paths[i] = data[i].getPath();
       expressions[i] = data[i].getExpression();
     }
-    assertEquals(Arrays.asList("address", "address.city", "address.street",
-        "person", "person.lastModified", "person.name", "person.readonly"),
-        Arrays.asList(paths));
+    assertEquals(Arrays.asList("address", "address.city", "address.street", "person", "person.has",
+        "person.is", "person.lastModified", "person.name", "person.readonly"), Arrays.asList(paths));
     // address is a property, person is a method in CompositeEditor
-    assertEquals(Arrays.asList("address", "address.city", "address.street",
-        "person()", "person().lastModified", "person().name",
+    assertEquals(Arrays.asList("address", "address.city", "address.street", "person()",
+        "person().has", "person().is", "person().lastModified", "person().name",
         "person().readonly"), Arrays.asList(expressions));
     assertTrue(data[0].isDelegateRequired());
     assertFalse(data[0].isLeafValueEditor() || data[0].isValueAwareEditor());
     assertTrue(data[3].isDelegateRequired());
     assertFalse(data[3].isLeafValueEditor() || data[3].isValueAwareEditor());
-    checkPersonLastModified(data[4]);
-    checkPersonName(data[5]);
-    checkPersonReadonly(data[6]);
+    int fieldNum = 4;
+    checkPersonHasHas(data[fieldNum++]);
+    checkPersonIsIs(data[fieldNum++]);
+    checkPersonLastModified(data[fieldNum++]);
+    checkPersonName(data[fieldNum++]);
+    checkPersonReadonly(data[fieldNum++]);
   }
 
   public void testCyclicDriver() {
@@ -259,14 +261,19 @@
     EditorModel m = new EditorModel(logger,
         types.findType("t.PersonEditorDriver"), rfedType);
     EditorData[] fields = m.getEditorData();
-    assertEquals(3, fields.length);
+    assertEquals(5, fields.length);
 
+    int fieldNum = 0;
+    // hasHas
+    checkPersonHasHas(fields[fieldNum++]);
+    // isIs
+    checkPersonIsIs(fields[fieldNum++]);
     // lastModified
-    checkPersonLastModified(fields[0]);
+    checkPersonLastModified(fields[fieldNum++]);
     // name
-    checkPersonName(fields[1]);
+    checkPersonName(fields[fieldNum++]);
     // readonly
-    checkPersonReadonly(fields[2]);
+    checkPersonReadonly(fields[fieldNum++]);
   }
 
   public void testFlatData() throws UnableToCompleteException {
@@ -282,10 +289,13 @@
     assertEquals("person", composite[1].getPropertyName());
 
     EditorData[] person = m.getEditorData(types.findType("t.PersonEditor"));
-    assertEquals(3, person.length);
-    assertEquals("lastModified", person[0].getPropertyName());
-    assertEquals("name", person[1].getPropertyName());
-    assertEquals("readonly", person[2].getPropertyName());
+    assertEquals(5, person.length);
+    int fieldNum = 0;
+    assertEquals("has", person[fieldNum++].getPropertyName());
+    assertEquals("is", person[fieldNum++].getPropertyName());
+    assertEquals("lastModified", person[fieldNum++].getPropertyName());
+    assertEquals("name", person[fieldNum++].getPropertyName());
+    assertEquals("readonly", person[fieldNum++].getPropertyName());
 
     EditorData[] address = m.getEditorData(types.findType("t.AddressEditor"));
     assertEquals("city", address[0].getPropertyName());
@@ -465,7 +475,29 @@
     EditorModel m = new EditorModel(logger,
         types.findType("t.PersonEditorWithAliasedSubEditorsDriver"), rfedType);
     EditorData[] fields = m.getEditorData();
-    assertEquals(8, fields.length);
+    assertEquals(12, fields.length);
+  }
+
+  private void checkPersonHasHas(EditorData editorField) {
+    assertNotNull(editorField);
+    assertEquals(types.findType(SimpleEditor.class.getName()),
+        editorField.getEditorType().isParameterized().getBaseType());
+    assertTrue(editorField.isLeafValueEditor());
+    assertFalse(editorField.isDelegateRequired());
+    assertFalse(editorField.isValueAwareEditor());
+    assertEquals(".hasHas()", editorField.getGetterExpression());
+    assertEquals("setHas", editorField.getSetterName());
+  }
+
+  private void checkPersonIsIs(EditorData editorField) {
+    assertNotNull(editorField);
+    assertEquals(types.findType(SimpleEditor.class.getName()),
+        editorField.getEditorType().isParameterized().getBaseType());
+    assertTrue(editorField.isLeafValueEditor());
+    assertFalse(editorField.isDelegateRequired());
+    assertFalse(editorField.isValueAwareEditor());
+    assertEquals(".isIs()", editorField.getGetterExpression());
+    assertEquals("setIs", editorField.getSetterName());
   }
 
   private void checkPersonLastModified(EditorData editorField) {
@@ -676,6 +708,10 @@
         code.append("String getName();\n");
         code.append("long getLastModified();\n");
         code.append("String getReadonly();\n");
+        code.append("boolean hasHas();\n");
+        code.append("boolean isIs();\n");
+        code.append("void setHas(boolean has);\n");
+        code.append("void setIs(boolean is);\n");
         code.append("void setName(String name);\n");
         code.append("void setLastModified(long value);\n");
         code.append("}");
@@ -689,6 +725,8 @@
         code.append("import " + Editor.class.getName() + ";\n");
         code.append("import " + SimpleEditor.class.getName() + ";\n");
         code.append("class PersonEditor implements Editor<PersonProxy> {\n");
+        code.append("SimpleEditor<Boolean> has;\n");
+        code.append("SimpleEditor<Boolean> is;\n");
         code.append("SimpleEditor<Long> lastModified;\n");
         code.append("public SimpleEditor<String> name;\n");
         code.append("SimpleEditor<String> readonly;\n");