Structural changes to UiBinder to make fields accessible via getters
and creation lazy loaded. LazyPanel support is also added.

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10059 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml b/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
index 9a57445..50cbc5b 100644
--- a/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
+++ b/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
@@ -15,18 +15,24 @@
 <!-- GWT UI Binder support.                                                 -->
 <module>
   <inherits name="com.google.gwt.resources.Resources" />
-  
+
   <source path="client"/>
   <source path="resources"/>
-  
-  <!-- By default UiBinder implementations are generated to use SafeHtmlTemplates 
-    to help protect against the introduction of cross-site scripting (XSS) attacks. 
-    This deprecated property can be used to disable that integration while the 
-    kinks are worked out. Its use is strongly discouraged, and the property will 
+
+  <!-- By default UiBinder implementations are generated to use SafeHtmlTemplates
+    to help protect against the introduction of cross-site scripting (XSS) attacks.
+    This deprecated property can be used to disable that integration while the
+    kinks are worked out. Its use is strongly discouraged, and the property will
     be removed in the near future. -->
   <define-configuration-property name="UiBinder.useSafeHtmlTemplates" is-multi-valued="false"/>
   <set-configuration-property name="UiBinder.useSafeHtmlTemplates" value="true"/>
-  
+
+  <!-- UiBinder can be configured to use a new strategy that enables a faster
+       rendering mode and make some widgets lazily created. This is still experimental
+       but should be the default option in a soon future. -->
+  <define-configuration-property name="UiBinder.useLazyWidgetBuilders" is-multi-valued="false"/>
+  <set-configuration-property name="UiBinder.useLazyWidgetBuilders" value="false"/>
+
   <generate-with class="com.google.gwt.uibinder.rebind.UiBinderGenerator">
     <when-type-assignable class="com.google.gwt.uibinder.client.UiBinder"/>
   </generate-with>
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java b/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java
index 9133cf9..7da4e8d 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java
@@ -45,8 +45,9 @@
  */
 public class FieldReferenceConverter {
   /**
-   * May be thrown by the {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate Delegate} for badly
-   * formatted input.
+   * May be thrown by the
+   * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate Delegate}
+   * for badly formatted input.
    */
   @SuppressWarnings("serial")
   public static class IllegalFieldReferenceException extends RuntimeException {
@@ -167,8 +168,15 @@
     StringBuilder b = new StringBuilder();
     String[] segments = value.split("[.]");
 
-    for (String segment : segments) {
-      segment = cssConverter.convertName(segment);
+    for (int i = 0; i < segments.length; ++i) {
+      String segment = cssConverter.convertName(segments[i]);
+
+      // The first segment is converted to a field getter. So,
+      // "bundle.whatever" becomes "get_bundle().whatever".
+      if (fieldManager != null && i == 0) {
+        segment = fieldManager.convertFieldToGetter(segment);
+      }
+
       if (b.length() == 0) {
         b.append(segment); // field name
       } else {
diff --git a/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java b/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
index 4b8c1ed..05cd36f 100644
--- a/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
+++ b/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
@@ -26,6 +26,28 @@
  * so please don't use them for non-UiBinder code.
  */
 public class UiBinderUtil {
+
+  /**
+   * A helper class to enable lazy creation of DOM elements.
+   */
+  public static class LazyDomElement {
+
+    private Element element;
+    private final String domId;
+
+    public LazyDomElement(String domId) {
+      this.domId = domId;
+    }
+
+    public Element get() {
+      if (element == null) {
+        element = Document.get().getElementById(domId).cast();
+        element.removeAttribute("id");
+      }
+      return element;
+    }
+  }
+
   /**
    * Temporary attachment record that keeps track of where an element was
    * before attachment.  Use the detach method to put things back.
@@ -34,15 +56,15 @@
   public static class TempAttachment {
     private final Element element;
     private final Element origParent;
-    private final Element origSibling;    
-    
-    private TempAttachment(Element origParent, Element origSibling, 
+    private final Element origSibling;
+
+    private TempAttachment(Element origParent, Element origSibling,
         Element element) {
       this.origParent = origParent;
       this.origSibling = origSibling;
       this.element = element;
     }
-    
+
     /**
      * Restore to previous DOM state before attachment.
      */
@@ -54,21 +76,21 @@
         orphan(element);
       }
     }
-  }  
-  
+  }
+
   private static Element hiddenDiv;
-  
+
   /**
-   * Attaches the element to the dom temporarily.  Keeps track of where it is 
+   * Attaches the element to the dom temporarily.  Keeps track of where it is
    * attached so that things can be put back latter.
-   * 
+   *
    * @return attachment record which can be used for reverting back to previous
    *         DOM state
    */
   public static TempAttachment attachToDom(Element element) {
     // TODO(rjrjr) This is copied from HTMLPanel. Reconcile
     ensureHiddenDiv();
-    
+
     // Hang on to the panel's original parent and sibling elements so that it
     // can be replaced.
     Element origParent = element.getParentElement();
@@ -76,7 +98,7 @@
 
     // Attach the panel's element to the hidden div.
     hiddenDiv.appendChild(element);
-    
+
     return new TempAttachment(origParent, origSibling, element);
   }
 
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
index f5e9f22..24d55c2 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
@@ -37,7 +37,7 @@
       throws UnableToCompleteException {
     String fieldName = writer.declareFieldIfNeeded(elem);
     if (fieldName != null) {
-      String token = writer.declareDomField(fieldName);
+      String token = writer.declareDomField(fieldName, element);
 
       if (elem.hasAttribute("id")) {
         writer.die(elem, String.format(
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
index a36ae2d..83d5d40 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HTMLPanelParser.java
@@ -61,7 +61,7 @@
     if (null == customTag) {
       writer.setFieldInitializerAsConstructor(fieldName, type, writer.declareTemplateCall(html));
     } else {
-      writer.setFieldInitializerAsConstructor(fieldName, type, customTag, 
+      writer.setFieldInitializerAsConstructor(fieldName, type, customTag,
           writer.declareTemplateCall(html));
     }
   }
@@ -72,9 +72,11 @@
    */
   private HtmlInterpreter makeHtmlInterpreter(final String fieldName,
       final UiBinderWriter uiWriter) {
-    final String ancestorExpression = fieldName + ".getElement()";
+    final String ancestorExpression = uiWriter.useLazyWidgetBuilders()
+        ? fieldName : (fieldName + ".getElement()");
 
-    PlaceholderInterpreterProvider placeholderInterpreterProvider = new PlaceholderInterpreterProvider() {
+    PlaceholderInterpreterProvider placeholderInterpreterProvider =
+        new PlaceholderInterpreterProvider() {
       public PlaceholderInterpreter get(MessageWriter message) {
         return new WidgetPlaceholderInterpreter(fieldName, uiWriter, message,
             ancestorExpression);
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
index d61fa40..562c11e 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
@@ -47,7 +47,8 @@
    */
   public static HtmlInterpreter newInterpreterForUiObject(
       UiBinderWriter writer, String uiExpression) {
-    String ancestorExpression = uiExpression + ".getElement()";
+    String ancestorExpression = writer.useLazyWidgetBuilders()
+        ? uiExpression : uiExpression + ".getElement()";
     return new HtmlInterpreter(writer, ancestorExpression,
         new HtmlMessageInterpreter(writer, ancestorExpression));
   }
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/LazyPanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/LazyPanelParser.java
new file mode 100644
index 0000000..96a36a8
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/elementparsers/LazyPanelParser.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.uibinder.elementparsers;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.XMLElement;
+import com.google.gwt.user.client.ui.LazyPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Parses {@link com.google.gwt.user.client.ui.LazyPanel} widgets.
+ */
+public class LazyPanelParser implements ElementParser {
+
+  private static final String INITIALIZER_FORMAT = "new %s() {\n"
+      + "  protected %s createWidget() {\n"
+      + "    return %s;\n"
+      + "  }\n"
+      + "}";
+
+  public void parse(XMLElement elem, String fieldName, JClassType type,
+      UiBinderWriter writer) throws UnableToCompleteException {
+
+    if (!writer.useLazyWidgetBuilders()) {
+      writer.die("LazyPanel only works with UiBinder.useLazyWidgetBuilders enabled.");
+    }
+
+    XMLElement child = elem.consumeSingleChildElement();
+    if (!writer.isWidgetElement(child)) {
+      writer.die(child, "Expecting only widgets in %s", elem);
+    }
+
+    String childFieldName = writer.parseElementToField(child);
+
+    String lazyPanelClassPath = LazyPanel.class.getName();
+    String widgetClassPath = Widget.class.getName();
+
+    String code = String.format(
+        INITIALIZER_FORMAT, lazyPanelClassPath, widgetClassPath, childFieldName);
+    writer.setFieldInitializer(fieldName, code);
+  }
+}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
index 7bbc33f..81784b3 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
@@ -16,6 +16,9 @@
 package com.google.gwt.uibinder.elementparsers;
 
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.uibinder.client.UiBinderUtil.LazyDomElement;
+import com.google.gwt.uibinder.rebind.FieldManager;
+import com.google.gwt.uibinder.rebind.FieldWriter;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 
@@ -66,7 +69,7 @@
     this.uiWriter = writer;
   }
 
-  public String interpretElement(XMLElement elem) 
+  public String interpretElement(XMLElement elem)
       throws UnableToCompleteException {
     if (uiWriter.isWidgetElement(elem)) {
       // Allocate a local variable to hold the dom id for this widget. Note
@@ -76,21 +79,43 @@
       String idHolder = uiWriter.declareDomIdHolder();
       String childField = uiWriter.parseElementToField(elem);
       uiWriter.ensureCurrentFieldAttached();
-      
+
       String elementPointer = idHolder + "Element";
       uiWriter.addInitStatement(
           "com.google.gwt.user.client.Element %s = " +
           "com.google.gwt.dom.client.Document.get().getElementById(%s).cast();",
           elementPointer, idHolder);
-      // Delay replacing the placeholders with the widgets until after 
-      // detachment so as not to end up attaching the widget to the DOM 
-      // unnecessarily
-      uiWriter.addDetachStatement(
-          "%1$s.addAndReplaceElement(%2$s, %3$s);", 
-          fieldName, childField, elementPointer);      
+
+      FieldManager fieldManager = uiWriter.getFieldManager();
+
+      if (uiWriter.useLazyWidgetBuilders()) {
+
+        // Register a DOM id field.
+        String lazyDomElementPath = LazyDomElement.class.getCanonicalName();
+        fieldManager.registerField(lazyDomElementPath, elementPointer)
+            .setInitializer(String.format("new %s(%s)",
+            lazyDomElementPath, fieldManager.convertFieldToGetter(idHolder)));
+
+        // Add attach/detach sections for this element.
+        FieldWriter fieldWriter = fieldManager.require(fieldName);
+        fieldWriter.addAttachStatement("%s.get();",
+            fieldManager.convertFieldToGetter(elementPointer));
+        fieldWriter.addDetachStatement("%s.addAndReplaceElement(%s, %s.get());",
+          fieldName, childField, fieldManager.convertFieldToGetter(elementPointer));
+
+      } else {
+
+        // Delay replacing the placeholders with the widgets until after
+        // detachment so as not to end up attaching the widget to the DOM
+        // unnecessarily
+        uiWriter.addDetachStatement(
+            "%1$s.addAndReplaceElement(%2$s, %3$s);",
+            fieldName, childField, elementPointer);
+      }
 
       // Create an element to hold the widget.
       String tag = getLegalPlaceholderTag(elem);
+      idHolder = fieldManager.convertFieldToGetter(idHolder);
       if (uiWriter.useSafeHtmlTemplates()) {
         idHolder = uiWriter.tokenForStringExpression(idHolder);
       } else {
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
index 803b9bf..5f9edde 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
@@ -18,6 +18,7 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.uibinder.rebind.FieldManager;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.messages.MessageWriter;
@@ -60,6 +61,7 @@
    * Other, but that seems more trouble than it's worth.
    */
 
+  private final FieldManager fieldManager;
   private int serial = 0;
   private final String ancestorExpression;
   private final String fieldName;
@@ -73,6 +75,7 @@
     super(writer, message, ancestorExpression);
     this.fieldName = fieldName;
     this.ancestorExpression = ancestorExpression;
+    this.fieldManager = uiWriter.getFieldManager();
  }
 
   @Override
@@ -114,14 +117,23 @@
   public String postProcess(String consumed) throws UnableToCompleteException {
     for (String idHolder : idToWidgetElement.keySet()) {
       XMLElement childElem = idToWidgetElement.get(idHolder);
-      String childField = uiWriter.parseElementToField(childElem);
+
+      String childField = uiWriter.parseElementToFieldWriter(childElem).getName();
 
       genSetWidgetTextCall(idHolder, childField);
-      uiWriter.addInitStatement("%1$s.addAndReplaceElement(%2$s, %3$s);",
-          fieldName, childField, idHolder);
+
+      if (uiWriter.useLazyWidgetBuilders()) {
+        fieldManager.require(fieldName).addDetachStatement(
+            "%1$s.addAndReplaceElement(%2$s, %3$s);",
+            fieldName, fieldManager.convertFieldToGetter(childField), idHolder);
+      } else {
+        uiWriter.addInitStatement("%1$s.addAndReplaceElement(%2$s, %3$s);",
+            fieldName, childField, idHolder);
+      }
     }
+
     /*
-     * We get used recursively, so this will be called again. Empty the map 
+     * We get used recursively, so this will be called again. Empty the map
      * or else we'll re-register things.
      */
     idToWidgetElement.clear();
@@ -134,8 +146,9 @@
   }
 
   private String genOpenTag(String name, String idHolder) {
+    idHolder = fieldManager.convertFieldToGetter(idHolder);
     if (uiWriter.useSafeHtmlTemplates()) {
-      idHolder = uiWriter.tokenForStringExpression(idHolder); 
+      idHolder = uiWriter.tokenForStringExpression(idHolder);
     } else {
       idHolder = "\" + " + idHolder + " + \"";
     }
@@ -144,16 +157,32 @@
     return openPlaceholder;
   }
 
-  private void genSetWidgetTextCall(String idHolder, String childField) {
-    if (idIsHasText.contains(idHolder)) {
-      uiWriter.addInitStatement(
-          "%s.setText(%s.getElementById(%s).getInnerText());", childField,
-          fieldName, idHolder);
-    }
-    if (idIsHasHTML.contains(idHolder)) {
-      uiWriter.addInitStatement(
-          "%s.setHTML(%s.getElementById(%s).getInnerHTML());", childField,
-          fieldName, idHolder);
+  private void genSetWidgetTextCall(String idHolder, String childField)
+      throws UnableToCompleteException {
+
+    if (uiWriter.useLazyWidgetBuilders()) {
+      if (idIsHasText.contains(idHolder)) {
+        fieldManager.require(childField).addAttachStatement(
+            "%s.setText(%s.getElementById(%s).getInnerText());", childField,
+            fieldManager.convertFieldToGetter(fieldName),
+            fieldManager.convertFieldToGetter(idHolder));
+      } else if (idIsHasHTML.contains(idHolder)) {
+        fieldManager.require(childField).addAttachStatement(
+            "%s.setHTML(%s.getElementById(%s).getInnerHTML());", childField,
+            fieldManager.convertFieldToGetter(fieldName),
+            fieldManager.convertFieldToGetter(idHolder));
+      }
+    } else {
+      if (idIsHasText.contains(idHolder)) {
+        uiWriter.addInitStatement(
+            "%s.setText(%s.getElementById(%s).getInnerText());", childField,
+            fieldName, idHolder);
+      }
+      if (idIsHasHTML.contains(idHolder)) {
+        uiWriter.addInitStatement(
+            "%s.setHTML(%s.getElementById(%s).getInnerHTML());", childField,
+            fieldName, idHolder);
+      }
     }
   }
 
@@ -186,8 +215,9 @@
   }
 
   private String handleOpaqueWidgetPlaceholder(String name, String idHolder) {
+    idHolder = fieldManager.convertFieldToGetter(idHolder);
     if (uiWriter.useSafeHtmlTemplates()) {
-      idHolder = uiWriter.tokenForStringExpression(idHolder); 
+      idHolder = uiWriter.tokenForStringExpression(idHolder);
     } else {
       idHolder = "\" + " + idHolder + " + \"";
     }
diff --git a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
index c61f284..b907124 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2009 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
@@ -20,7 +20,11 @@
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.uibinder.rebind.model.OwnerField;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -33,15 +37,26 @@
  * {@link FieldWriter#getInstantiableType()}.
  */
 abstract class AbstractFieldWriter implements FieldWriter {
-  private static final String NO_DEFAULT_CTOR_ERROR = "%1$s has no default (zero args) constructor. To fix this, you can define"
+  private static final String NO_DEFAULT_CTOR_ERROR =
+      "%1$s has no default (zero args) constructor. To fix this, you can define"
       + " a @UiFactory method on the UiBinder's owner, or annotate a constructor of %2$s with"
       + " @UiConstructor.";
 
-  private final String name;
+  private static int nextAttachVar;
+
+  public static String getNextAttachVar() {
+    return "attachRecord" + nextAttachVar++;
+  }
 
   private final Set<FieldWriter> needs = new LinkedHashSet<FieldWriter>();
+  private final List<String> statements = new ArrayList<String>();
+  private final List<String> attachStatements = new ArrayList<String>();
+  private final List<String> detachStatements = new ArrayList<String>();
+
+  private final String name;
   private String initializer;
   private boolean written;
+  private int buildPrecedence;
   private MortalLogger logger;
 
   public AbstractFieldWriter(String name, MortalLogger logger) {
@@ -50,12 +65,38 @@
     }
     this.name = name;
     this.logger = logger;
+    this.buildPrecedence = 1;
+  }
+
+  @Override
+  public void addAttachStatement(String format, Object... args) {
+    attachStatements.add(String.format(format, args));
+  }
+
+  @Override
+  public void addDetachStatement(String format, Object... args) {
+    detachStatements.add(String.format(format, args));
+  }
+
+  @Override
+  public void addStatement(String format, Object... args) {
+    statements.add(String.format(format, args));
+  }
+
+  @Override
+  public int getBuildPrecedence() {
+    return buildPrecedence;
   }
 
   public String getInitializer() {
     return initializer;
   }
 
+  @Override
+  public String getName() {
+    return name;
+  }
+
   public JType getReturnType(String[] path, MonitoredLogger logger) {
     if (!name.equals(path[0])) {
       throw new RuntimeException(this
@@ -70,6 +111,11 @@
     needs.add(f);
   }
 
+  @Override
+  public void setBuildPrecendence(int precedence) {
+    this.buildPrecedence = precedence;
+  }
+
   public void setInitializer(String initializer) {
     this.initializer = initializer;
   }
@@ -114,6 +160,113 @@
     this.written = true;
   }
 
+  @Override
+  public void writeFieldBuilder(IndentedWriter w, int getterCount,
+    OwnerField ownerField) throws UnableToCompleteException {
+    if (getterCount > 1) {
+      w.write("%s;  // more than one getter call detected. Precedence: %s",
+            FieldManager.getFieldBuilder(name), getBuildPrecedence());
+      return;
+    }
+
+    if (getterCount == 0 && ownerField != null) {
+      w.write("%s;  // no getter call detected but must bind to ui:field. Precedence: %s",
+          FieldManager.getFieldBuilder(name), getBuildPrecedence());
+    }
+  }
+
+  @Override
+  public void writeFieldDefinition(IndentedWriter w, TypeOracle typeOracle,
+      OwnerField ownerField, DesignTimeUtils designTime, int getterCount)
+      throws UnableToCompleteException {
+
+    // Check initializer.
+    if (initializer == null) {
+      if (ownerField != null && ownerField.isProvided()) {
+        initializer = String.format("owner.%s", name);
+      } else {
+        JClassType type = getInstantiableType();
+        if (type != null) {
+          if ((type.isInterface() == null)
+              && (type.findConstructor(new JType[0]) == null)) {
+            logger.die(NO_DEFAULT_CTOR_ERROR, type.getQualifiedSourceName(),
+                type.getName());
+          }
+        }
+        initializer = String.format("(%1$s) GWT.create(%1$s.class)",
+            getQualifiedSourceName());
+      }
+    }
+
+    w.newline();
+    w.write("/**");
+    w.write(" * Getter for %s called %s times.", name, getterCount);
+    w.write(" */");
+    if (getterCount > 1) {
+      w.write("private %1$s %2$s;", getQualifiedSourceName(), name);
+    }
+
+    w.write("private %s %s {", getQualifiedSourceName(), FieldManager.getFieldGetter(name));
+    w.indent();
+    w.write("return %s;", (getterCount > 1) ? name : FieldManager.getFieldBuilder(name));
+    w.outdent();
+    w.write("}");
+
+    w.write("private %s %s {", getQualifiedSourceName(), FieldManager.getFieldBuilder(name));
+    w.indent();
+
+    w.write("// Creation section.");
+    if (getterCount > 1) {
+      w.write("%s = %s;", name, initializer);
+    } else {
+      w.write("%s %s = %s;", getQualifiedSourceName(), name, initializer);
+    }
+
+    w.write("// Setup section.");
+    for (String s : statements) {
+      w.write(s);
+    }
+
+    String attachedVar = null;
+
+    if (attachStatements.size() > 0) {
+      w.newline();
+      w.write("// Attach section.");
+      attachedVar = getNextAttachVar();
+
+      JClassType elementType = typeOracle.findType(Element.class.getName());
+
+      String elementToAttach = getInstantiableType().isAssignableTo(elementType)
+          ? name : name + ".getElement()";
+
+      w.write("UiBinderUtil.TempAttachment %s = UiBinderUtil.attachToDom(%s);",
+          attachedVar, elementToAttach);
+
+      for (String s : attachStatements) {
+        w.write(s);
+      }
+    }
+
+    if (attachedVar != null) {
+      w.newline();
+      w.write("// Detach section.");
+      w.write("%s.detach();", attachedVar);
+      for (String s : detachStatements) {
+        w.write(s);
+      }
+    }
+
+    if ((ownerField != null) && !ownerField.isProvided()) {
+      w.newline();
+      w.write("owner.%1$s = %1$s;", name);
+    }
+
+    w.newline();
+    w.write("return %s;", name);
+    w.outdent();
+    w.write("}");
+  }
+
   private JMethod findMethod(JClassType type, String methodName) {
     // TODO Move this and getClassHierarchyBreadthFirst to JClassType
     for (JClassType nextType : UiBinderWriter.getClassHierarchyBreadthFirst(type)) {
@@ -153,4 +306,4 @@
     }
     return type;
   }
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
index d508b62..26489ae 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2009 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
@@ -15,14 +15,18 @@
  */
 package com.google.gwt.uibinder.rebind;
 
-import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
+import com.google.gwt.uibinder.rebind.model.OwnerClass;
 
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.Map;
@@ -35,6 +39,21 @@
 
   private static final String DUPLICATE_FIELD_ERROR = "Duplicate declaration of field %1$s.";
 
+  private static final Comparator<FieldWriter> BUILD_DEFINITION_SORT =
+      new Comparator<FieldWriter>() {
+    public int compare(FieldWriter field1, FieldWriter field2) {
+      return field2.getBuildPrecedence() - field1.getBuildPrecedence();
+    }
+  };
+
+  public static String getFieldBuilder(String fieldName) {
+    return String.format("build_%s()", fieldName);
+  }
+
+  public static String getFieldGetter(String fieldName) {
+    return String.format("get_%s()", fieldName);
+  }
+
   private final TypeOracle types;
   private final MortalLogger logger;
 
@@ -42,18 +61,67 @@
    * Map of field name to FieldWriter. Note its a LinkedHashMap--we want to
    * write these out in the order they're declared.
    */
-  private final LinkedHashMap<String, FieldWriter> fieldsMap = new LinkedHashMap<String, FieldWriter>();
+  private final LinkedHashMap<String, FieldWriter> fieldsMap =
+      new LinkedHashMap<String, FieldWriter>();
 
   /**
    * A stack of the fields.
    */
   private final LinkedList<FieldWriter> parsedFieldStack = new LinkedList<FieldWriter>();
 
-  private LinkedHashMap<String, FieldReference> fieldReferences = new LinkedHashMap<String, FieldReference>();
+  private LinkedHashMap<String, FieldReference> fieldReferences =
+      new LinkedHashMap<String, FieldReference>();
 
-  public FieldManager(TypeOracle types, MortalLogger logger) {
+  /**
+   * Counts the number of times a getter field is called, this important to
+   * decide which strategy to take when outputing getters and builders.
+   * {@see com.google.gwt.uibinder.rebind.FieldWriter#writeFieldDefinition}.
+   */
+  private final Map<String, Integer> gettersCounter = new HashMap<String, Integer>();
+
+  /**
+   * Whether to use the new strategy of generating UiBinder code.
+   */
+  private final boolean useLazyWidgetBuilders;
+
+  public FieldManager(TypeOracle types, MortalLogger logger, boolean useLazyWidgetBuilders) {
     this.types = types;
     this.logger = logger;
+    this.useLazyWidgetBuilders = useLazyWidgetBuilders;
+  }
+
+  /**
+   * Converts the given field to its getter. Example:
+   *  <li> myWidgetX = get_myWidgetX()
+   *  <li> f_html1 = get_f_html1()
+   */
+  public String convertFieldToGetter(String fieldName) {
+    // TODO(hermes, rjrjr, rdcastro): revisit this and evaluate if this
+    // conversion can be made directly in FieldWriter.
+    if (!useLazyWidgetBuilders) {
+      return fieldName;
+    }
+
+    int count = getGetterCounter(fieldName) + 1;
+    gettersCounter.put(fieldName, count);
+    return getFieldGetter(fieldName);
+  }
+
+  /**
+   * Initialize with field builders the generated <b>Widgets</b> inner class.
+   * {@see com.google.gwt.uibinder.rebind.FieldWriter#writeFieldBuilder}.
+   */
+  public void initializeWidgetsInnerClass(IndentedWriter w,
+      OwnerClass ownerClass) throws UnableToCompleteException {
+
+    FieldWriter[] fields = fieldsMap.values().toArray(
+        new FieldWriter[fieldsMap.size()]);
+    Arrays.sort(fields, BUILD_DEFINITION_SORT);
+
+    for (FieldWriter field : fields) {
+      int count = getGetterCounter(field.getName());
+      field.writeFieldBuilder(w, count, ownerClass.getUiField(field.getName()));
+    }
   }
 
   /**
@@ -87,9 +155,9 @@
    * When making a field we peek at the {@link #parsedFieldStack} to make sure
    * that the field that holds the widget currently being parsed will depended
    * upon the field being declared. This ensures, for example, that dom id
-   * fields (see {@link UiBinderWriter#declareDomIdHolder()}) used by an HTMLPanel 
+   * fields (see {@link UiBinderWriter#declareDomIdHolder()}) used by an HTMLPanel
    * will be declared before it is.
-   * 
+   *
    * @param fieldType the type of the new field
    * @param fieldName the name of the new field
    * @return a new {@link FieldWriter} instance
@@ -102,6 +170,11 @@
     return registerField(fieldName, field);
   }
 
+  public FieldWriter registerField(String type, String fieldName)
+      throws UnableToCompleteException {
+    return registerField(types.findType(type), fieldName);
+  }
+
   /**
    * Used to declare fields that will hold generated instances generated
    * CssResource interfaces. If your field will hold a reference of an existing
@@ -114,7 +187,7 @@
    * upon the field being declared. This ensures, for example, that dom id
    * fields (see {@link UiBinderWriter#declareDomIdHolder()}) used by an HTMLPanel
    * will be declared before it is.
-   * 
+   *
    * @throws UnableToCompleteException on duplicate name
    * @return a new {@link FieldWriter} instance
    */
@@ -136,7 +209,7 @@
    * upon the field being declared. This ensures, for example, that dom id
    * fields (see {@link UiBinderWriter#declareDomIdHolder()}) used by an HTMLPanel
    * will be declared before it is.
-   * 
+   *
    * @param assignableType class or interface extened or implemented by this
    *          type
    * @param typeName the full qualified name for the class associated with the
@@ -169,10 +242,23 @@
   }
 
   /**
+   * Gets a FieldWriter given its name or throws a RuntimeException if not found.
+   * @param fieldName the name of the {@link FieldWriter} to find
+   * @return the {@link FieldWriter} instance indexed by fieldName
+   */
+  public FieldWriter require(String fieldName) {
+    FieldWriter fieldWriter = lookup(fieldName);
+    if (fieldWriter == null) {
+      throw new RuntimeException("The required field %s doesn't exist.");
+    }
+    return fieldWriter;
+  }
+
+  /**
    * To be called after parsing is complete. Surveys all
    * <code>{field.reference}</code>s and checks they refer to existing types,
    * and have appropriate return types.
-   * 
+   *
    * @throws UnableToCompleteException if any <code>{field.references}</code>
    *           can't be resolved
    */
@@ -192,8 +278,23 @@
   }
 
   /**
+   * Outputs the getter and builder definitions for all fields.
+   * {@see com.google.gwt.uibinder.rebind.AbstractFieldWriter#writeFieldDefinition}.
+   */
+  public void writeFieldDefinitions(IndentedWriter writer, TypeOracle typeOracle,
+      OwnerClass ownerClass, DesignTimeUtils designTime)
+      throws UnableToCompleteException {
+    Collection<FieldWriter> fields = fieldsMap.values();
+    for (FieldWriter field : fields) {
+      int counter = getGetterCounter(field.getName());
+      field.writeFieldDefinition(writer, typeOracle,
+          ownerClass.getUiField(field.getName()), designTime, counter);
+    }
+  }
+
+  /**
    * Writes all stored gwt fields.
-   * 
+   *
    * @param writer the writer to output
    * @param ownerTypeName the name of the class being processed
    */
@@ -205,6 +306,14 @@
     }
   }
 
+  /**
+   * Gets the number of times a getter for the given field is called.
+   */
+  private int getGetterCounter(String fieldName) {
+    Integer count = gettersCounter.get(fieldName);
+    return (count == null) ? 0 : count;
+  }
+
   private FieldWriter registerField(String fieldName, FieldWriter field)
       throws UnableToCompleteException {
     requireUnique(fieldName);
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
index 6cabcd3..7ad8a7e 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
@@ -18,6 +18,8 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.uibinder.rebind.model.OwnerField;
 
 /**
  * Models a field to be written in the generated binder code. Note that this is
@@ -36,6 +38,50 @@
 public interface FieldWriter {
 
   /**
+   * Add a statement to be executed right after the current field is attached.
+   *
+   * <pre>
+   *   HTMLPanel panel = new HTMLPanel(
+   *      "<span id='someId' class='someclass'></span>...");
+   *
+   *   // statement section.
+   *   widgetX.setStyleName("someCss");
+   *   widgetX.setVisible(true);
+   *
+   *   // attach section.
+   *   UiBinderUtil.TempAttachment attachRecord =
+   *      UiBinderUtil.attachToDom(panel.getElement());
+   *   get_domId0Element().get();
+   *   get_domId1Element().get();
+   *
+   *   // detach section.
+   *   attachRecord.detach();
+   *   panel.addAndReplaceElement(get_someWidget(), get_domId0Element().get());
+   *   panel.addAndReplaceElement(get_otherWidget(), get_domId1Element().get());
+   * </pre>
+   */
+  void addAttachStatement(String format, Object... args);
+
+  /**
+   * Add a statement to be executed right after the current field is detached.
+   * {@see #addAttachStatement}.
+   */
+  void addDetachStatement(String format, Object... args);
+
+  /**
+   * Add a statement for the given field, executed right after its creation. Example:
+   *
+   * <pre>
+   *   WidgetX widgetX = GWT.create(WidgetX.class);
+   *
+   *   // statement section.
+   *   widgetX.setStyleName("someCss");
+   *   widgetX.setVisible(true);
+   * </pre>
+   */
+  void addStatement(String format, Object... args);
+
+  /**
    * Returns the type of this field, or for generated types the type it extends.
    */
   // TODO(rjrjr) When ui:style is able to implement multiple interfaces,
@@ -43,6 +89,11 @@
   JClassType getAssignableType();
 
   /**
+   * Gets this field builder precedence.
+   */
+  int getBuildPrecedence();
+
+  /**
    * Returns the custom initializer for this field, or null if it is not set.
    */
   String getInitializer();
@@ -54,6 +105,11 @@
   JClassType getInstantiableType();
 
   /**
+   * Get the name of the field.
+   */
+  String getName();
+
+  /**
    * Returns the qualified source name of this type.
    */
   String getQualifiedSourceName();
@@ -71,6 +127,12 @@
   void needs(FieldWriter f);
 
   /**
+   * Sets the precedence of this field builder. Field with higher values are
+   * written first.
+   */
+  void setBuildPrecendence(int precedence);
+
+  /**
    * Used to provide an initializer string to use instead of a
    * {@link com.google.gwt.core.client.GWT#create} call. Note that this is an
    * RHS expression. Don't include the leading '=', and don't end it with ';'.
@@ -83,4 +145,62 @@
    * Write the field declaration.
    */
   void write(IndentedWriter w) throws UnableToCompleteException;
+
+  /**
+   * Write this field builder in the <b>Widgets</b> inner class. There are 3
+   * possible situations:
+   *
+   * <dl>
+   *   <dt>getter never called</dt>
+   *     <dd>write builder only if there's a ui:field associated</dd>
+   *   <dt>getter called only once</dt>
+   *     <dd>don't need to write the builder since the getter fallback to the
+   *      builder when called</dd>
+   *   <dt>getter called more than once</dt>
+   *     <dd>in this case a field class is created, the builder is written
+   *      and the getter returns the field class</dd>
+   * </dl>
+   *
+   * {@see com.google.gwt.uibinder.rebind.FieldWriter#writeFieldGetter}.
+   */
+  void writeFieldBuilder(IndentedWriter w, int getterCount, OwnerField ownerField)
+      throws UnableToCompleteException;
+
+  /**
+   * Output the getter and builder definitions for the given field.
+   *
+   *  <p>
+   *  <b>Example for widgets called only once:</b>
+   *  <pre>
+   *  private WidgetX get_widgetX() {
+   *    return build_widgetX();
+   *  }
+   *  private WidgetX build_widgetX() {
+   *   widget = GWT.create(WidgetX.class);
+   *   widget.setStyleName("css");
+   *   return widgetX;
+   *  }
+   *  </pre>
+   *  Notice that there's no field and the getter just fallback to the builder.
+   *  </p>
+   *
+   *  <p><b>Example for widgets called more than once:</b>
+   *  <pre>
+   *  private WidgetX widgetX;
+   *  private WidgetX get_widgetX() {
+   *    return widgetX;
+   *  }
+   *  private WidgetX build_widgetX() {
+   *   widget = GWT.create(WidgetX.class);
+   *   widget.setStyleName("css");
+   *   return widgetX;
+   *  }
+   * </pre>
+   * Notice that the getter just returns the field. The builder is called in
+   * the Widgets ctor.
+   * </p>
+   */
+  void writeFieldDefinition(IndentedWriter w, TypeOracle typeOracle,
+      OwnerField ownerField, DesignTimeUtils designTime, int getterCount)
+      throws UnableToCompleteException;
 }
diff --git a/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java b/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
index 311f18b..83ae24e 100644
--- a/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
@@ -65,7 +65,8 @@
  */
 class HandlerEvaluator {
 
-  private static final String HANDLER_BASE_NAME = "handlerMethodWithNameVeryUnlikelyToCollideWithUserFieldNames";
+  private static final String HANDLER_BASE_NAME =
+      "handlerMethodWithNameVeryUnlikelyToCollideWithUserFieldNames";
   /*
    * TODO(rjrjr) The correct fix is to put the handlers in a locally defined
    * class, making the generated code look like this
@@ -82,6 +83,7 @@
   private final JClassType handlerRegistrationJClass;
   private final JClassType eventHandlerJClass;
   private final OwnerClass ownerClass;
+  private final boolean useLazyWidgetBuilders;
 
   /**
    * The verbose testable constructor.
@@ -90,9 +92,11 @@
    * @param logger the logger for warnings and errors
    * @param oracle the type oracle
    */
-  HandlerEvaluator(OwnerClass ownerClass, MortalLogger logger, TypeOracle oracle) {
+  HandlerEvaluator(OwnerClass ownerClass, MortalLogger logger,
+      TypeOracle oracle, boolean useLazyWidgetBuilders) {
     this.ownerClass = ownerClass;
     this.logger = logger;
+    this.useLazyWidgetBuilders = useLazyWidgetBuilders;
 
     handlerRegistrationJClass = oracle.findType(HandlerRegistration.class.getName());
     eventHandlerJClass = oracle.findType(EventHandler.class.getName());
@@ -156,8 +160,8 @@
         }
 
         // Cool to tie the handler into the object.
-        writeAddHandler(writer, handlerVarName, addHandlerMethodType.getName(),
-            objectName);
+        writeAddHandler(writer, fieldManager, handlerVarName,
+            addHandlerMethodType.getName(), objectName);
       }
     }
   }
@@ -215,10 +219,16 @@
    *          the object
    * @param objectName the name of the object we want to tie the handler
    */
-  void writeAddHandler(IndentedWriter writer, String handlerVarName,
-      String addHandlerMethodName, String objectName) {
-    writer.write("%1$s.%2$s(%3$s);", objectName, addHandlerMethodName,
-        handlerVarName);
+  void writeAddHandler(IndentedWriter writer, FieldManager fieldManager,
+      String handlerVarName, String addHandlerMethodName, String objectName)
+      throws UnableToCompleteException {
+    if (useLazyWidgetBuilders) {
+      fieldManager.require(objectName).addStatement("%1$s.%2$s(%3$s);",
+          objectName, addHandlerMethodName, handlerVarName);
+    } else {
+      writer.write("%1$s.%2$s(%3$s);", objectName, addHandlerMethodName,
+          handlerVarName);
+    }
   }
 
   /**
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
index 10c56a4..8eace4e 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 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
@@ -48,7 +48,8 @@
   private static final String TEMPLATE_SUFFIX = ".ui.xml";
 
   private static final String XSS_SAFE_CONFIG_PROPERTY = "UiBinder.useSafeHtmlTemplates";
-  
+  private static final String LAZY_WIDGET_BUILDERS_PROPERTY = "UiBinder.useLazyWidgetBuilders";
+
   /**
    * Given a UiBinder interface, return the path to its ui.xml file, suitable
    * for any classloader to find it as a resource.
@@ -127,10 +128,30 @@
     return packageName + "." + implName;
   }
 
+  private Boolean extractConfigProperty(MortalLogger logger,
+      PropertyOracle propertyOracle, String configProperty, boolean defaultValue) {
+    List<String> values;
+    try {
+      values = propertyOracle.getConfigurationProperty(configProperty).getValues();
+    } catch (BadPropertyValueException e) {
+      logger.warn("No value found for configuration property %s.", configProperty);
+      return defaultValue;
+    }
+
+    String value = values.get(0);
+    if (!value.equals(Boolean.FALSE.toString()) && !value.equals(Boolean.TRUE.toString())) {
+      logger.warn("Unparseable value \"%s\" found for configuration property %s", value,
+          configProperty);
+      return defaultValue;
+    }
+
+    return Boolean.valueOf(value);
+  }
+
   private void generateOnce(JClassType interfaceType, String implName,
       PrintWriter binderPrintWriter, TreeLogger treeLogger, TypeOracle oracle,
-      ResourceOracle resourceOracle, PropertyOracle propertyOracle, 
-      PrintWriterManager writerManager,  DesignTimeUtils designTime) 
+      ResourceOracle resourceOracle, PropertyOracle propertyOracle,
+      PrintWriterManager writerManager,  DesignTimeUtils designTime)
   throws UnableToCompleteException {
 
     MortalLogger logger = new MortalLogger(treeLogger);
@@ -138,10 +159,12 @@
     MessagesWriter messages = new MessagesWriter(BINDER_URI, logger,
         templatePath, interfaceType.getPackage().getName(), implName);
 
+    boolean useLazyWidgetBuilders = useLazyWidgetBuilders(logger, propertyOracle);
+    FieldManager fieldManager = new FieldManager(oracle, logger, useLazyWidgetBuilders);
+
     UiBinderWriter uiBinderWriter = new UiBinderWriter(interfaceType, implName,
-        templatePath, oracle, logger, new FieldManager(oracle, logger),
-        messages, designTime, uiBinderCtx, 
-        useSafeHtmlTemplates(logger, propertyOracle));
+        templatePath, oracle, logger, fieldManager, messages, designTime, uiBinderCtx,
+        useSafeHtmlTemplates(logger, propertyOracle), useLazyWidgetBuilders);
 
     Document doc = getW3cDoc(logger, designTime, resourceOracle, templatePath);
     designTime.rememberPathForElements(doc);
@@ -183,23 +206,13 @@
     return doc;
   }
 
+  private Boolean useLazyWidgetBuilders(MortalLogger logger, PropertyOracle propertyOracle) {
+    return extractConfigProperty(logger, propertyOracle, LAZY_WIDGET_BUILDERS_PROPERTY, false);
+  }
+
   private Boolean useSafeHtmlTemplates(MortalLogger logger, PropertyOracle propertyOracle) {
-    List<String> values;
-    try {
-      values = propertyOracle.getConfigurationProperty(XSS_SAFE_CONFIG_PROPERTY).getValues();
-    } catch (BadPropertyValueException e) {
-      logger.warn("No value found for configuration property %s.", XSS_SAFE_CONFIG_PROPERTY);
-      return true;
-    }
-
-    String value = values.get(0);
-    if (!value.equals(Boolean.FALSE.toString()) && !value.equals(Boolean.TRUE.toString())) {
-      logger.warn("Unparseable value \"%s\" found for configuration property %s", value,
-          XSS_SAFE_CONFIG_PROPERTY);
-      return true;
-    }
-
-    Boolean rtn = Boolean.valueOf(value);
+    Boolean rtn = extractConfigProperty(
+        logger, propertyOracle, XSS_SAFE_CONFIG_PROPERTY, true);
 
     if (!rtn) {
       logger.warn("Configuration property %s is false! UiBinder SafeHtml integration is off, "
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
index b723f9a..4cf8a47 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -171,7 +171,8 @@
         source);
     FieldWriter field = fieldManager.registerField(dataResourceType,
         dataMethod.getName());
-    field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(),
+    field.setInitializer(String.format("%s.%s()",
+        fieldManager.convertFieldToGetter(bundleClass.getFieldName()),
         dataMethod.getName()));
   }
 
@@ -200,7 +201,8 @@
 
     FieldWriter field = fieldManager.registerField(imageResourceType,
         imageMethod.getName());
-    field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(),
+    field.setInitializer(String.format("%s.%s()",
+        fieldManager.convertFieldToGetter(bundleClass.getFieldName()),
         imageMethod.getName()));
   }
 
@@ -350,7 +352,8 @@
         publicType, body, importTypes);
 
     FieldWriter field = fieldManager.registerFieldForGeneratedCssResource(cssMethod);
-    field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(),
+    field.setInitializer(String.format("%s.%s()",
+        fieldManager.convertFieldToGetter(bundleClass.getFieldName()),
         cssMethod.getName()));
   }
 
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 528a024..5fb9ce9 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -65,7 +65,8 @@
 
   // TODO(rjrjr) Another place that we need a general anonymous field
   // mechanism
-  private static final String CLIENT_BUNDLE_FIELD = "clientBundleFieldNameUnlikelyToCollideWithUserSpecifiedFieldOkay";
+  private static final String CLIENT_BUNDLE_FIELD =
+      "clientBundleFieldNameUnlikelyToCollideWithUserSpecifiedFieldOkay";
 
   public static String asCommaSeparatedList(String... args) {
     StringBuilder b = new StringBuilder();
@@ -176,7 +177,7 @@
   private final MessagesWriter messages;
   private final DesignTimeUtils designTime;
   private final Tokenator tokenator = new Tokenator();
-  
+
   private final String templatePath;
   private final TypeOracle oracle;
   /**
@@ -198,7 +199,9 @@
   private final FieldManager fieldManager;
 
   private final ImplicitClientBundle bundleClass;
-  
+
+  private final boolean useLazyWidgetBuilders;
+
   private final boolean useSafeHtmlTemplates;
 
   private int domId = 0;
@@ -233,7 +236,7 @@
       String templatePath, TypeOracle oracle, MortalLogger logger,
       FieldManager fieldManager, MessagesWriter messagesWriter,
       DesignTimeUtils designTime, UiBinderContext uiBinderCtx,
-      boolean useSafeHtmlTemplates)
+      boolean useSafeHtmlTemplates, boolean useLazyWidgetBuilders)
       throws UnableToCompleteException {
     this.baseClass = baseClass;
     this.implClassName = implClassName;
@@ -245,6 +248,7 @@
     this.designTime = designTime;
     this.uiBinderCtx = uiBinderCtx;
     this.useSafeHtmlTemplates = useSafeHtmlTemplates;
+    this.useLazyWidgetBuilders = useLazyWidgetBuilders;
 
     // Check for possible misuse 'GWT.create(UiBinder.class)'
     JClassType uibinderItself = oracle.findType(UiBinder.class.getCanonicalName());
@@ -273,7 +277,8 @@
     ownerClass = new OwnerClass(uiOwnerType, logger, uiBinderCtx);
     bundleClass = new ImplicitClientBundle(baseClass.getPackage().getName(),
         this.implClassName, CLIENT_BUNDLE_FIELD, logger);
-    handlerEvaluator = new HandlerEvaluator(ownerClass, logger, oracle);
+    handlerEvaluator = new HandlerEvaluator(
+        ownerClass, logger, oracle, useLazyWidgetBuilders);
 
     attributeParsers = new AttributeParsers(oracle, fieldManager, logger);
     bundleParsers = new BundleAttributeParsers(oracle, logger, getOwnerClass(),
@@ -292,7 +297,7 @@
   public void addDetachStatement(String format, Object... args) {
     detachStatementsStack.getFirst().add(String.format(format, args));
   }
-  
+
   /**
    * Add a statement to be run after everything has been instantiated, in the
    * style of {@link String#format}.
@@ -306,7 +311,21 @@
    * of {@link String#format}.
    */
   public void addStatement(String format, Object... args) {
-    statements.add(formatCode(format, args));
+    String code = formatCode(format, args);
+
+    if (useLazyWidgetBuilders) {
+      /**
+       * I'm intentionally over-simplifying this and assuming that the input
+       * comes always in the format: field.somestatement();
+       * Thus, field can be extracted easily and the element parsers don't
+       * need to be changed all at once.
+       */
+      int idx = code.indexOf(".");
+      String fieldName = code.substring(0, idx);
+      fieldManager.require(fieldName).addStatement(format, args);
+    } else {
+      statements.add(code);
+    }
   }
 
   /**
@@ -338,18 +357,34 @@
    * call and assign the Element instance to its field.
    *
    * @param fieldName The name of the field being declared
+   * @param ancestorField The name of fieldName parent
    */
-  public String declareDomField(String fieldName)
+  public String declareDomField(String fieldName, String ancestorField)
       throws UnableToCompleteException {
     ensureAttached();
     String name = declareDomIdHolder();
-    setFieldInitializer(fieldName, "null");
-    addInitStatement(
-        "%s = com.google.gwt.dom.client.Document.get().getElementById(%s).cast();",
-        fieldName, name);
-    addInitStatement("%s.removeAttribute(\"id\");", fieldName);
 
-    return tokenForStringExpression(name);
+    if (useLazyWidgetBuilders) {
+      // Initialize and add the removeAttribute('id') statement for the new
+      // DOM field.
+      FieldWriter field = fieldManager.require(fieldName);
+      field.setInitializer(formatCode(
+          "com.google.gwt.dom.client.Document.get().getElementById(%s).cast()",
+          fieldManager.convertFieldToGetter(name)));
+      field.addStatement("%s.removeAttribute(\"id\");", fieldName);
+
+      // The dom must be created by its ancestor.
+      fieldManager.require(ancestorField).addAttachStatement(
+          fieldManager.convertFieldToGetter(fieldName) + ";");
+    } else {
+      setFieldInitializer(fieldName, "null");
+      addInitStatement(
+          "%s = com.google.gwt.dom.client.Document.get().getElementById(%s).cast();",
+          fieldName, name);
+      addInitStatement("%s.removeAttribute(\"id\");", fieldName);
+    }
+
+    return tokenForStringExpression(fieldManager.convertFieldToGetter(name));
   }
 
   /**
@@ -363,6 +398,10 @@
     FieldWriter domField = fieldManager.registerField(
         oracle.findType(String.class.getName()), domHolderName);
     domField.setInitializer("com.google.gwt.dom.client.Document.get().createUniqueId()");
+
+    // Dom IDs must be created first, that's why it gets a higher builder precedence.
+    domField.setBuildPrecendence(2);
+
     return domHolderName;
   }
 
@@ -409,25 +448,25 @@
 
   /**
    * Writes a new SafeHtml template to the generated BinderImpl.
-   * 
+   *
    * @return The invocation of the SafeHtml template function with the arguments
    * filled in
    */
-  public String declareTemplateCall(String html) 
+  public String declareTemplateCall(String html)
     throws IllegalArgumentException {
     if (!useSafeHtmlTemplates) {
       return '"' + html + '"';
     }
-    
+
     return htmlTemplates.addSafeHtmlTemplate(html, tokenator);
   }
 
   /**
    * Given a string containing tokens returned by {@link #tokenForStringExpression},
-   * {@link #tokenForSafeHtmlExpression} or {@link #declareDomField}, return a 
-   * string with those tokens replaced by the appropriate expressions. (It is 
-   * not normally necessary for an {@link XMLElement.Interpreter} or 
-   * {@link ElementParser} to make this call, as the tokens are typically 
+   * {@link #tokenForSafeHtmlExpression} or {@link #declareDomField}, return a
+   * string with those tokens replaced by the appropriate expressions. (It is
+   * not normally necessary for an {@link XMLElement.Interpreter} or
+   * {@link ElementParser} to make this call, as the tokens are typically
    * replaced by the TemplateWriter itself.)
    */
   public String detokenate(String betokened) {
@@ -588,7 +627,11 @@
   public DesignTimeUtils getDesignTime() {
     return designTime;
   }
-  
+
+  public FieldManager getFieldManager() {
+    return fieldManager;
+  }
+
   /**
    * Returns the logger, at least until we get get it handed off to parsers via
    * constructor args.
@@ -640,6 +683,30 @@
    */
   public String parseElementToField(XMLElement elem)
       throws UnableToCompleteException {
+    /**
+     * TODO(hermes,rjrjr,rdcastro): seems bad we have to run
+     * parseElementToFieldWriter(), get the field writer and
+     * then call fieldManager.convertFieldToGetter().
+     *
+     * Can't we move convertFieldToGetter() to FieldWriter?
+     *
+     * The current answer is no because convertFieldToGetter() might be called
+     * before a given FieldWriter is actually created.
+     */
+    FieldWriter field = parseElementToFieldWriter(elem);
+    return fieldManager.convertFieldToGetter(field.getName());
+  }
+
+  /**
+   * Parses the object associated with the specified element, and returns the
+   * field writer that will hold it. The element is likely to make recursive
+   * calls back to this method to have its children parsed.
+   *
+   * @param elem the xml element to be parsed
+   * @return the field holder just created
+   */
+  public FieldWriter parseElementToFieldWriter(XMLElement elem)
+      throws UnableToCompleteException {
     if (elementParsers.isEmpty()) {
       registerParsers();
     }
@@ -662,7 +729,8 @@
       parser.parse(elem, fieldName, type, this);
     }
     fieldManager.pop();
-    return fieldName;
+
+    return field;
   }
 
   /**
@@ -690,10 +758,10 @@
   }
 
   /**
-   * Like {@link #tokenForStringExpression}, but used for runtime expressions 
-   * that we trust to be safe to interpret at runtime as HTML without escaping, 
-   * like translated messages with simple formatting. Wrapped in a call to 
-   * {@link com.google.gwt.safehtml.shared.SafeHtmlUtils#fromSafeConstant} to 
+   * Like {@link #tokenForStringExpression}, but used for runtime expressions
+   * that we trust to be safe to interpret at runtime as HTML without escaping,
+   * like translated messages with simple formatting. Wrapped in a call to
+   * {@link com.google.gwt.safehtml.shared.SafeHtmlUtils#fromSafeConstant} to
    * keep the expression from being escaped by the SafeHtml template.
    *
    * @param expression
@@ -702,9 +770,9 @@
     if (!useSafeHtmlTemplates) {
       return tokenForStringExpression(expression);
     }
-    
+
     String token =  tokenator.nextToken("SafeHtmlUtils.fromSafeConstant(" +
-        expression + ")");      
+        expression + ")");
     htmlTemplates.noteSafeConstant("SafeHtmlUtils.fromSafeConstant(" +
         expression + ")");
     return token;
@@ -724,6 +792,10 @@
     return tokenator.nextToken(("\" + " + expression + " + \""));
   }
 
+  public boolean useLazyWidgetBuilders() {
+    return useLazyWidgetBuilders;
+  }
+
   /**
    * @return true of SafeHtml integration is in effect
    */
@@ -737,7 +809,7 @@
   public void warn(String message) {
     logger.warn(message);
   }
-  
+
   /**
    * Post a warning message.
    */
@@ -751,7 +823,7 @@
   public void warn(XMLElement context, String message, Object... params) {
     logger.warn(context, message, params);
   }
-  
+
   /**
    * Entry point for the code generation logic. It generates the
    * implementation's superstructure, and parses the root widget (leading to all
@@ -1000,7 +1072,18 @@
     StringWriter stringWriter = new StringWriter();
     IndentedWriter niceWriter = new IndentedWriter(
         new PrintWriter(stringWriter));
-    writeBinder(niceWriter, rootField);
+
+    if (useLazyWidgetBuilders) {
+      for (ImplicitCssResource css : bundleClass.getCssMethods()) {
+        String fieldName = css.getName();
+        FieldWriter cssField = fieldManager.require(fieldName);
+        cssField.addStatement("%s.ensureInjected();", fieldName);
+        cssField.setBuildPrecendence(2);
+      }
+      writeBinderForAttachableStrategy(niceWriter, rootField);
+    } else {
+      writeBinder(niceWriter, rootField);
+    }
 
     ensureAttachmentCleanedUp();
     return stringWriter.toString();
@@ -1061,6 +1144,7 @@
     addWidgetParser("HasAlignment");
     addWidgetParser("DateLabel");
     addWidgetParser("NumberLabel");
+    addWidgetParser("LazyPanel");
   }
 
   /**
@@ -1087,7 +1171,7 @@
     writeClassOpen(w);
     writeStatics(w);
     w.newline();
-    
+
     // Create SafeHtml Template
     writeSafeHtmlTemplates(w);
 
@@ -1100,7 +1184,7 @@
 
     writeGwtFields(w);
     w.newline();
-    
+
     designTime.writeAttributes(this);
     writeAddedStatements(w);
     w.newline();
@@ -1124,6 +1208,73 @@
     w.write("}");
   }
 
+  /**
+   * Writes a different optimized UiBinder's source for the attachable
+   * strategy.
+   */
+  private void writeBinderForAttachableStrategy(
+      IndentedWriter w, String rootField) throws UnableToCompleteException {
+    writePackage(w);
+
+    writeImports(w);
+    w.newline();
+
+    writeClassOpen(w);
+    writeStatics(w);
+    w.newline();
+
+    // Create SafeHtml Template
+    writeSafeHtmlTemplates(w);
+
+    w.newline();
+
+    // createAndBindUi method
+    w.write("public %s createAndBindUi(final %s owner) {",
+        uiRootType.getParameterizedQualifiedSourceName(),
+        uiOwnerType.getParameterizedQualifiedSourceName());
+    w.indent();
+    w.newline();
+
+    designTime.writeAttributes(this);
+    w.newline();
+
+    w.write("return new Widgets(owner).%s;", rootField);
+    w.outdent();
+    w.write("}");
+
+    // Writes the inner class Widgets.
+    w.newline();
+    w.write("/**");
+    w.write(" * Encapsulates the access to all inner widgets");
+    w.write(" */");
+    w.write("class Widgets {");
+    w.indent();
+
+    String ownerClassType = uiOwnerType.getParameterizedQualifiedSourceName();
+    w.write("private final %s owner;", ownerClassType);
+    w.newline();
+
+    writeHandlers(w);
+    w.newline();
+
+    w.write("public Widgets(final %s owner) {", ownerClassType);
+    w.indent();
+    w.write("this.owner = owner;");
+    fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
+    w.outdent();
+    w.write("}");
+
+    fieldManager.writeFieldDefinitions(
+        w, getOracle(), getOwnerClass(), getDesignTime());
+
+    w.outdent();
+    w.write("}");
+
+    // Close class
+    w.outdent();
+    w.write("}");
+  }
+
   private void writeClassOpen(IndentedWriter w) {
     w.write("public class %s implements UiBinder<%s, %s>, %s {", implClassName,
         uiRootType.getParameterizedQualifiedSourceName(),
@@ -1253,21 +1404,21 @@
       w.write("package %1$s;", packageName);
       w.newline();
     }
-  }  
-  
+  }
+
   /**
-   * Write statements created by {@link HtmlTemplates#addSafeHtmlTemplate}. This 
+   * Write statements created by {@link HtmlTemplates#addSafeHtmlTemplate}. This
    * code must be placed after all instantiation code.
    */
   private void writeSafeHtmlTemplates(IndentedWriter w) {
     if (!(htmlTemplates.isEmpty())) {
       assert useSafeHtmlTemplates : "SafeHtml is off, but templates were made.";
-      
+
       w.write("interface Template extends SafeHtmlTemplates {");
       w.indent();
 
       htmlTemplates.writeTemplates(w);
-      
+
       w.outdent();
       w.write("}");
       w.newline();
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java b/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java
index 8aea5b1..8e6f83a 100644
--- a/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java
+++ b/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java
@@ -108,7 +108,7 @@
     elemProvider = new XMLElementProviderImpl(new AttributeParsers(types, null,
         logger), bundleParsers, types, logger, DesignTimeUtilsStub.EMPTY);
 
-    fieldManager = new FieldManager(types, logger);
+    fieldManager = new FieldManager(types, logger, false);
     JClassType baseType = types.findType("my.Ui.BaseClass");
     MessagesWriter messages = new MessagesWriter(BINDER_URI, logger,
         templatePath, baseType.getPackage().getName(), implName);
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java b/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java
index 7264f1a..ad145ca 100644
--- a/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java
+++ b/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java
@@ -36,7 +36,7 @@
       TypeOracle oracle, MortalLogger logger, FieldManager fieldManager,
       MessagesWriter messagesWriter) throws UnableToCompleteException {
     super(baseClass, implClassName, templatePath, oracle, logger, fieldManager, messagesWriter,
-        DesignTimeUtilsStub.EMPTY, new UiBinderContext(), true);
+        DesignTimeUtilsStub.EMPTY, new UiBinderContext(), true, false);
   }
 
   @Override
diff --git a/user/test/com/google/gwt/uibinder/rebind/HandlerEvaluatorTest.java b/user/test/com/google/gwt/uibinder/rebind/HandlerEvaluatorTest.java
index 28f9cee..98de89b 100644
--- a/user/test/com/google/gwt/uibinder/rebind/HandlerEvaluatorTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/HandlerEvaluatorTest.java
@@ -44,6 +44,7 @@
   private OwnerClass ownerType;
   private MortalLogger logger;
   private TypeOracle oracle;
+  private FieldManager fieldManager;
 
   @Override
   protected void setUp() throws Exception {
@@ -55,6 +56,7 @@
     mockControl = EasyMock.createControl();
     ownerType = mockControl.createMock(OwnerClass.class);
     oracle = mockControl.createMock(TypeOracle.class);
+    fieldManager = mockControl.createMock(FieldManager.class);
 
     // TODO(hermes): sucks I know!!!! This class shouldn't be using EasyMock
     // but for now that's the easiest way of creating new instances of
@@ -70,7 +72,7 @@
         eventHandlerJClass);
 
     mockControl.replay();
-    evaluator = new HandlerEvaluator(ownerType, logger, oracle);
+    evaluator = new HandlerEvaluator(ownerType, logger, oracle, false);
     mockControl.verify();
     mockControl.reset();
   }
@@ -78,7 +80,7 @@
   public void testWriteAddHandler() throws Exception {
     StringWriter sw = new StringWriter();
     evaluator.writeAddHandler(new IndentedWriter(new PrintWriter(sw)),
-        "handler1", "addClickHandler", "label1");
+        fieldManager, "handler1", "addClickHandler", "label1");
 
     assertEquals("label1.addClickHandler(handler1);", sw.toString().trim());
   }