Re-roll r9549 with a fix that ensures that sub-editors of a LeafValueEditor will be ignored.
Patch by: t.broyer, bobv
Review by: bobv, rjrjr

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9555 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 e11df64..0ae18b1 100644
--- a/user/src/com/google/gwt/editor/rebind/model/EditorModel.java
+++ b/user/src/com/google/gwt/editor/rebind/model/EditorModel.java
@@ -28,6 +28,7 @@
 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 java.util.ArrayList;
 import java.util.Arrays;
@@ -60,7 +61,7 @@
         if (compositeEditorIntf.equals(parameterized.getBaseType())) {
           JClassType[] typeArgs = parameterized.getTypeArgs();
           assert typeArgs.length == 3;
-          return new JClassType[]{typeArgs[1], typeArgs[2]};
+          return new JClassType[] {typeArgs[1], typeArgs[2]};
         }
       }
     }
@@ -175,6 +176,8 @@
    */
   private final JGenericType isEditorIntf;
 
+  private final JGenericType leafValueEditorIntf;
+
   private final TreeLogger logger;
 
   private final EditorModel parentModel;
@@ -216,6 +219,8 @@
     assert isEditorIntf != null : "No IsEditor type";
     compositeEditorIntf = oracle.findType(CompositeEditor.class.getName()).isGenericType();
     assert compositeEditorIntf != null : "No CompositeEditor type";
+    leafValueEditorIntf = oracle.findType(LeafValueEditor.class.getName()).isGenericType();
+    assert leafValueEditorIntf != null;
 
     JClassType[] interfaces = intf.getImplementedInterfaces();
     if (interfaces.length != 1) {
@@ -244,6 +249,7 @@
     this.editorType = editorType;
     this.editorSoFar = subEditor;
     this.isEditorIntf = parent.isEditorIntf;
+    this.leafValueEditorIntf = parent.leafValueEditorIntf;
     this.parentModel = parent;
     this.proxyType = proxyType;
     this.typeData = parent.typeData;
@@ -301,41 +307,44 @@
     List<EditorData> flatData = new ArrayList<EditorData>();
     List<EditorData> toReturn = new ArrayList<EditorData>();
 
-    for (JClassType type : editorType.getFlattenedSupertypeHierarchy()) {
-      for (JField field : type.getFields()) {
-        if (field.isPrivate() || field.isStatic()
-            || field.getAnnotation(Editor.Ignore.class) != null) {
-          continue;
-        }
-        JType fieldClassType = field.getType();
-        if (shouldExamine(fieldClassType)) {
-          List<EditorData> data = createEditorData(EditorAccess.via(field));
-          accumulateEditorData(data, flatData, toReturn);
-        }
-      }
-      for (JMethod method : type.getMethods()) {
-        if (method.isPrivate() || method.isStatic()
-            || method.getAnnotation(Editor.Ignore.class) != null) {
-          continue;
-        }
-        JType methodReturnType = method.getReturnType();
-        if (shouldExamine(methodReturnType)
-            && method.getParameters().length == 0) {
-          EditorAccess access = EditorAccess.via(method);
-          if (access.getPath().equals("as")
-              && isEditorIntf.isAssignableFrom(editorType)) {
-            // Ignore IsEditor.asEditor()
-            continue;
-          } else if (access.getPath().equals("createEditorForTraversal")
-              && compositeEditorIntf.isAssignableFrom(editorType)) {
-            // Ignore CompositeEditor.createEditorForTraversal();
+    // Only look for sub-editor accessors if the editor isn't a leaf
+    if (!leafValueEditorIntf.isAssignableFrom(editorType)) {
+      for (JClassType type : editorType.getFlattenedSupertypeHierarchy()) {
+        for (JField field : type.getFields()) {
+          if (field.isPrivate() || field.isStatic()
+              || field.getAnnotation(Editor.Ignore.class) != null) {
             continue;
           }
-          List<EditorData> data = createEditorData(access);
-          accumulateEditorData(data, flatData, toReturn);
+          JType fieldClassType = field.getType();
+          if (shouldExamine(fieldClassType)) {
+            List<EditorData> data = createEditorData(EditorAccess.via(field));
+            accumulateEditorData(data, flatData, toReturn);
+          }
         }
+        for (JMethod method : type.getMethods()) {
+          if (method.isPrivate() || method.isStatic()
+              || method.getAnnotation(Editor.Ignore.class) != null) {
+            continue;
+          }
+          JType methodReturnType = method.getReturnType();
+          if (shouldExamine(methodReturnType)
+              && method.getParameters().length == 0) {
+            EditorAccess access = EditorAccess.via(method);
+            if (access.getPath().equals("as")
+                && isEditorIntf.isAssignableFrom(editorType)) {
+              // Ignore IsEditor.asEditor()
+              continue;
+            } else if (access.getPath().equals("createEditorForTraversal")
+                && compositeEditorIntf.isAssignableFrom(editorType)) {
+              // Ignore CompositeEditor.createEditorForTraversal();
+              continue;
+            }
+            List<EditorData> data = createEditorData(access);
+            accumulateEditorData(data, flatData, toReturn);
+          }
+        }
+        type = type.getSuperclass();
       }
-      type = type.getSuperclass();
     }
 
     if (compositeEditorIntf.isAssignableFrom(editorType)) {
@@ -423,7 +432,7 @@
       superModel = superModel.parentModel;
     }
 
-    if (!data.isLeafValueEditor()) {
+    if (data.isDelegateRequired()) {
       EditorModel subModel = new EditorModel(this, data.getEditorType(), data,
           calculateEditedType(logger, data.getEditorType()));
       accumulator.addAll(accumulator.indexOf(data) + 1,
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 564b02f..25e82f3 100644
--- a/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
+++ b/user/test/com/google/gwt/editor/rebind/model/EditorModelTest.java
@@ -144,6 +144,40 @@
     assertEquals(types.findType("t.PersonProxy"), m.getProxyType());
   }
 
+  /**
+   * Verify that we correctly descend into a subeditor of a CompositeEditor that
+   * also is a LeafValueEditor (as is the case of OptionalFieldEditor).
+   */
+  public void testCompositeAndLeafValueEditor()
+      throws UnableToCompleteException {
+    EditorModel m = new EditorModel(logger,
+        types.findType("t.CompositeAndLeafEditorDriver"), rfedType);
+
+    assertEquals(types.findType("t.CompositeAndLeafEditorDriver.AProxy"),
+        m.getProxyType());
+    assertEquals(types.findType("t.CompositeAndLeafEditorDriver.AEditor"),
+        m.getEditorType());
+
+    EditorData[] data = m.getEditorData();
+    assertEquals(1, data.length);
+
+    assertTrue(data[0].isCompositeEditor());
+
+    EditorData composed = data[0].getComposedData();
+    assertEquals(types.findType("t.CompositeAndLeafEditorDriver.BProxy"),
+        composed.getEditedType());
+    assertEquals(types.findType("t.CompositeAndLeafEditorDriver.BEditor"),
+        composed.getEditorType());
+
+    // Nonsensical for the optional editor to have any data
+    EditorData[] optionalEditorData = m.getEditorData(data[0].getEditorType());
+    assertEquals(0, optionalEditorData.length);
+
+    // Make sure we have EditorData for the sub-editor
+    EditorData[] subEditorData = m.getEditorData(composed.getEditorType());
+    assertEquals(1, subEditorData.length);
+  }
+
   public void testCompositeDriver() throws UnableToCompleteException {
     EditorModel m = new EditorModel(logger,
         types.findType("t.CompositeEditorDriver"), rfedType);
@@ -796,6 +830,42 @@
         code.append("}");
         return code;
       }
+    }, new MockJavaResource("t.CompositeAndLeafEditorDriver") {
+      /*
+       * Tests that we descend into sub-editor of a CompositeEditor that also is
+       * a LeafValueEditor (this is the case for the
+       * c.g.g.editor.client.adapters.OptionalFieldEditor). Also test that any
+       * editor-like fields within the LeafValueEditor are ignored.
+       */
+      @Override
+      protected CharSequence getContent() {
+        StringBuilder code = new StringBuilder();
+        code.append("package t;\n");
+        code.append("import " + Editor.class.getName() + ";\n");
+        code.append("import " + IsEditor.class.getName() + ";\n");
+        code.append("import " + EntityProxy.class.getName() + ";\n");
+        code.append("import " + RequestFactoryEditorDriver.class.getName()
+            + ";\n");
+        code.append("import " + SimpleEditor.class.getName() + ";\n");
+        code.append("import " + CompositeEditor.class.getName() + ";\n");
+        code.append("import " + LeafValueEditor.class.getName() + ";\n");
+        code.append("interface CompositeAndLeafEditorDriver extends"
+            + " RequestFactoryEditorDriver<CompositeAndLeafEditorDriver.AProxy,"
+            + " CompositeAndLeafEditorDriver.AEditor> {\n");
+        code.append("  interface AProxy extends EntityProxy { BProxy getB();}");
+        code.append("  interface BProxy extends EntityProxy { String getString();}");
+        code.append("  interface AEditor extends Editor<AProxy> {");
+        code.append("    OptionalBEditor bEditor();");
+        code.append("  }");
+        code.append("  interface OptionalBEditor extends CompositeEditor<BProxy, BProxy, BEditor>, LeafValueEditor<BProxy> {");
+        code.append("    LeafValueEditor<String> ignored();");
+        code.append("  }");
+        code.append("  interface BEditor extends Editor<BProxy> {");
+        code.append("    @Editor.Path(\"string\") SimpleEditor<String> coEditor();");
+        code.append("  }");
+        code.append("}");
+        return code;
+      }
     }, new MockJavaResource("java.util.List") {
         // Tests a Driver interface that extends more than RFED
       @Override
@@ -809,7 +879,7 @@
     }};
 
     Set<Resource> toReturn = new HashSet<Resource>(Arrays.asList(javaFiles));
-    toReturn.addAll(Arrays.asList(new Resource[]{
+    toReturn.addAll(Arrays.asList(new Resource[] {
         new RealJavaResource(CompositeEditor.class),
         new RealJavaResource(Editor.class),
         new RealJavaResource(EditorError.class),