Fixes design time support for @UiField.

It turns out that previous patch was not enough.  In new one instead
of preventing failing when no owner or field found, we disable
assigning to fields and support for event handlers at all. However we
can not disable them globally, for all UiBinder templates, we should
do this only for template under design (because other templates may be
used on it). So, I've changed a little way for choosing
DesignTimeUtils implementation.

http://gwt-code-reviews.appspot.com/834802

Review by: rjrjr@google.com


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8814 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java
index 253c3e3..4ca3e48 100644
--- a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java
+++ b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtils.java
@@ -34,13 +34,6 @@
   String getImplName(String implName);
 
   /**
-   * @return the source to check that "owner" is not <code>null</code>. Problem
-   *         is that at design time we render template without owner, so can not
-   *         provide it.
-   */
-  String getOwnerCheck();
-
-  /**
    * @return the path of given {@link Element}.
    */
   String getPath(Element element);
@@ -58,6 +51,13 @@
   void handleUIObject(Statements writer, XMLElement elem, String fieldName);
 
   /**
+   * @return <code>true</code> if this template is under design now, so some of
+   *         UiBinder features should be disables. This includes assigning
+   *         values into "@UiField", processing "@UiHandler".
+   */
+  boolean isDesignTime();
+
+  /**
    * Remembers value of attribute, for given {@link XMLElement}.
    */
   void putAttribute(XMLElement elem, String name, String value);
@@ -74,16 +74,6 @@
   void rememberPathForElements(Document doc);
 
   /**
-   * @return <code>true</code> if absence of "ui:field" attribute for
-   *         corresponding "@UiField" declaration is OK. Problem is that at
-   *         design time we create {@link ClassLoader} only once and can not
-   *         refresh Java type. So, when user asks to remove "ui:field" we
-   *         update both template and Java, but generator does not know about
-   *         Java change.
-   */
-  boolean shouldIgnoreNoUiFieldAttribute();
-
-  /**
    * Writes remembered values of attributes.
    */
   void writeAttributes(Statements writer);
diff --git a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsImpl.java b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsImpl.java
index c6a74f8..d0c1f94 100644
--- a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsImpl.java
+++ b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsImpl.java
@@ -20,6 +20,7 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import java.beans.Beans;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -27,6 +28,20 @@
  * Design time implementation of {@link DesignTimeUtils}.
  */
 public class DesignTimeUtilsImpl implements DesignTimeUtils {
+  /**
+   * @return <code>true</code> if given "Binder" is under design now. We should
+   *         not use "design time" globally, because while one widget in under
+   *         design, it may use other widgets and we want to execute them as is,
+   *         without design time tweaks.
+   */
+  public static boolean isDesignTime(String fqInterfaceName) {
+    if (Beans.isDesignTime()) {
+      String key = "gwt.UiBinder.isDesignTime " + fqInterfaceName;
+      return System.getProperty(key) != null;
+    }
+    return false;
+  }
+
   private final Map<Element, String> elementPaths = new HashMap<Element, String>();
   private final Map<String, String> attributes = new HashMap<String, String>();
 
@@ -51,10 +66,6 @@
     return implName + "_designTime" + System.currentTimeMillis();
   }
 
-  public String getOwnerCheck() {
-    return "if (owner != null) ";
-  }
-
   public String getPath(Element element) {
     return elementPaths.get(element);
   }
@@ -70,6 +81,10 @@
         elem.getDesignTimePath(), fieldName);
   }
 
+  public boolean isDesignTime() {
+    return true;
+  }
+
   public void putAttribute(XMLElement elem, String name, String value) {
     String path = elem.getDesignTimePath();
     String key = path + " " + name;
@@ -96,10 +111,6 @@
     rememberPathForElements(doc.getDocumentElement(), "0");
   }
 
-  public boolean shouldIgnoreNoUiFieldAttribute() {
-    return true;
-  }
-
   public void writeAttributes(Statements writer) {
     for (Map.Entry<String, String> entry : attributes.entrySet()) {
       String key = entry.getKey();
diff --git a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsStub.java b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsStub.java
index 06d783d..6459e4e 100644
--- a/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsStub.java
+++ b/user/src/com/google/gwt/uibinder/rebind/DesignTimeUtilsStub.java
@@ -31,10 +31,6 @@
     return implName;
   }
 
-  public String getOwnerCheck() {
-    return "";
-  }
-
   public String getPath(Element element) {
     return null;
   }
@@ -47,6 +43,10 @@
       String fieldName) {
   }
 
+  public boolean isDesignTime() {
+    return false;
+  }
+
   public void putAttribute(XMLElement elem, String name, String value) {
   }
 
@@ -56,10 +56,6 @@
   public void rememberPathForElements(Document doc) {
   }
 
-  public boolean shouldIgnoreNoUiFieldAttribute() {
-    return false;
-  }
-
   public void writeAttributes(Statements writer) {
   }
 }
\ No newline at end of file
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
index f90e5db..57710e2 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -32,7 +32,6 @@
 import org.w3c.dom.Document;
 import org.xml.sax.SAXParseException;
 
-import java.beans.Beans;
 import java.io.PrintWriter;
 
 /**
@@ -102,7 +101,7 @@
     }
 
     DesignTimeUtils designTime;
-    if (Beans.isDesignTime()) {
+    if (DesignTimeUtilsImpl.isDesignTime(fqInterfaceName)) {
       designTime = new DesignTimeUtilsImpl();
     } else {
       designTime = DesignTimeUtilsStub.EMPTY;
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 9a17ca5..78f96e6 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -896,8 +896,8 @@
       /*
        * And initialize the field
        */
-      niceWriter.write(designTime.getOwnerCheck() + "owner.%1$s = %2$s;",
-          ownerField.getName(), templateField);
+      niceWriter.write("owner.%1$s = %2$s;", ownerField.getName(),
+          templateField);
     } else {
       /*
        * But with @UiField(provided=true) the user builds it, so reverse the
@@ -1103,6 +1103,9 @@
   }
 
   private void writeHandlers(IndentedWriter w) throws UnableToCompleteException {
+    if (designTime.isDesignTime()) {
+      return;
+    }
     handlerEvaluator.run(w, fieldManager, "owner");
   }
 
@@ -1129,6 +1132,9 @@
    */
   private void writeOwnerFieldSetters(IndentedWriter niceWriter)
       throws UnableToCompleteException {
+    if (designTime.isDesignTime()) {
+      return;
+    }
     for (OwnerField ownerField : getOwnerClass().getUiFields()) {
       String fieldName = ownerField.getName();
       FieldWriter fieldWriter = fieldManager.lookup(fieldName);
@@ -1155,11 +1161,9 @@
 
       } else {
         // ownerField was not found as bundle resource or widget, must die.
-        if (!designTime.shouldIgnoreNoUiFieldAttribute()) {
-          die("Template %s has no %s attribute for %s.%s#%s", templatePath,
-              getUiFieldAttributeName(), uiOwnerType.getPackage().getName(),
-              uiOwnerType.getName(), fieldName);
-        }
+        die("Template %s has no %s attribute for %s.%s#%s", templatePath,
+            getUiFieldAttributeName(), uiOwnerType.getPackage().getName(),
+            uiOwnerType.getName(), fieldName);
       }
     }
   }
diff --git a/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java b/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java
index d87ed51..ef5eacb 100644
--- a/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/DesignTimeUtilsTest.java
@@ -24,6 +24,7 @@
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
+import java.beans.Beans;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -39,6 +40,35 @@
   private final DesignTimeUtilsImpl impl = new DesignTimeUtilsImpl();
 
   /**
+   * Test for {@link DesignTimeUtilsImpl#isDesignTime(String)}.
+   */
+  public void test_isDesignTime_evaluate() throws Exception {
+    // not design time
+    {
+      Beans.setDesignTime(false);
+      assertEquals(false, DesignTimeUtilsImpl.isDesignTime("no.Matter"));
+    }
+    // not this Binder
+    try {
+      Beans.setDesignTime(true);
+      System.setProperty("gwt.UiBinder.isDesignTime my.Binder", "true");
+      assertEquals(false, DesignTimeUtilsImpl.isDesignTime("other.Binder"));
+    } finally {
+      Beans.setDesignTime(false);
+      System.clearProperty("gwt.UiBinder.isDesignTime my.design.Binder");
+    }
+    // OK
+    try {
+      Beans.setDesignTime(true);
+      System.setProperty("gwt.UiBinder.isDesignTime my.Binder", "true");
+      assertEquals(true, DesignTimeUtilsImpl.isDesignTime("my.Binder"));
+    } finally {
+      Beans.setDesignTime(false);
+      System.clearProperty("gwt.UiBinder.isDesignTime my.design.Binder");
+    }
+  }
+
+  /**
    * Test for {@link DesignTimeUtils#addDeclarations(IndentedWriter)}.
    */
   public void test_addDeclarations_default() throws Exception {
@@ -185,17 +215,17 @@
   }
 
   /**
-   * Test for {@link DesignTimeUtils#getOwnerCheck()}.
+   * Test for {@link DesignTimeUtils#isDesignTime()}.
    */
-  public void test_getOwnerCheck_default() throws Exception {
-    assertEquals("", stub.getOwnerCheck());
+  public void test_isDesignTime_default() throws Exception {
+    assertEquals(false, stub.isDesignTime());
   }
 
   /**
-   * Test for {@link DesignTimeUtils#getOwnerCheck()}.
+   * Test for {@link DesignTimeUtils#isDesignTime()}.
    */
   public void test_getOwnerCheck_designTime() throws Exception {
-    assertEquals("if (owner != null) ", impl.getOwnerCheck());
+    assertEquals(true, impl.isDesignTime());
   }
 
   /**
@@ -324,18 +354,4 @@
       statements.add(String.format(format, args));
     }
   };
-
-  /**
-   * Test for {@link DesignTimeUtils#shouldIgnoreNoUiFieldAttribute()}.
-   */
-  public void test_shouldIgnoreNoUiFieldAttribute_default() throws Exception {
-    assertFalse(stub.shouldIgnoreNoUiFieldAttribute());
-  }
-
-  /**
-   * Test for {@link DesignTimeUtils#shouldIgnoreNoUiFieldAttribute()}.
-   */
-  public void test_shouldIgnoreNoUiFieldAttribute_designTime() throws Exception {
-    assertTrue(impl.shouldIgnoreNoUiFieldAttribute());
-  }
 }