Adding the 2.1.1-rc1 tag


git-svn-id: https://google-web-toolkit.googlecode.com/svn/tags/2.1.1-rc1@9368 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/eclipse/samples/Validation/.checkstyle b/eclipse/samples/Validation/.checkstyle
deleted file mode 100644
index 0f1cbd9..0000000
--- a/eclipse/samples/Validation/.checkstyle
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<fileset-config file-format-version="1.2.0" simple-config="true">
-    <fileset name="all" enabled="true" check-config-name="GWT Checks" local="false">
-        <file-match-pattern match-pattern="." include-pattern="true"/>
-    </fileset>
-    <filter name="NonSrcDirs" enabled="true"/>
-</fileset-config>
diff --git a/eclipse/samples/Validation/.classpath b/eclipse/samples/Validation/.classpath
deleted file mode 100644
index b67ec19..0000000
--- a/eclipse/samples/Validation/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="core/src"/>
-	<classpathentry kind="src" output="war" path="core/war"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/gwt-user"/>
-	<classpathentry kind="var" path="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA-sources.jar"/>
-	<classpathentry kind="var" path="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA.jar" sourcepath="/GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA-sources.jar"/>
-	<classpathentry kind="output" path="war/WEB-INF/classes"/>
-</classpath>
diff --git a/eclipse/samples/Validation/.project b/eclipse/samples/Validation/.project
deleted file mode 100644
index 93a5b2f..0000000
--- a/eclipse/samples/Validation/.project
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Validation</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>
-	</natures>
-	<linkedResources>
-		<link>
-			<name>core</name>
-			<type>2</type>
-			<locationURI>GWT_ROOT/samples/validation</locationURI>
-		</link>
-	</linkedResources>
-</projectDescription>
diff --git a/eclipse/samples/Validation/Validation-gwtc.launch b/eclipse/samples/Validation/Validation-gwtc.launch
deleted file mode 100644
index c48be56..0000000
--- a/eclipse/samples/Validation/Validation-gwtc.launch
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;Validation&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/Validation/core/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-user/core/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-user/core/super&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-dev/core/super&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;Validation&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.Compiler"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="com.google.gwt.sample.validation.Validation"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Validation"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea&#13;&#10;-Xmx256M&#13;&#10;-Dgwt.devjar=${gwt_devjar}"/>
-</launchConfiguration>
diff --git a/eclipse/samples/Validation/Validation.launch b/eclipse/samples/Validation/Validation.launch
deleted file mode 100644
index 45c184b..0000000
--- a/eclipse/samples/Validation/Validation.launch
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;Validation&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/Validation/core/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-user/core/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-user/core/super&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-dev/core/super&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;Validation&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.HostedMode"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-startupUrl Validation.html&#13;&#10;com.google.gwt.sample.validation.Validation"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Validation"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea&#13;&#10;-Xmx256M&#13;&#10;-Dgwt.devjar=${gwt_devjar}"/>
-</launchConfiguration>
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/console/Console.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/console/Console.java
deleted file mode 100644
index 0db3125..0000000
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/console/Console.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2010 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.sample.dynatablerf.console;
-
-import com.google.gwt.event.shared.SimpleEventBus;
-import com.google.gwt.requestfactory.server.testing.RequestFactoryMagic;
-import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.sample.dynatablerf.shared.AddressProxy;
-import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
-import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * A proof-of-concept to demonstrate how RequestFactory can be used from
- * non-client code.
- */
-public class Console {
-  public static void main(String[] args) {
-    String url = "http://localhost:8888/gwtRequest";
-    if (args.length == 1) {
-      url = args[0];
-    }
-    try {
-      new Console(new URI(url)).exec();
-      System.exit(0);
-    } catch (URISyntaxException e) {
-      System.err.println("Could not parse argument");
-    }
-    System.exit(1);
-  }
-
-  private final DynaTableRequestFactory rf;
-
-  private Console(URI uri) {
-    /*
-     * Instantiation of the RequestFactory interface uses the
-     * RequestFactoryMagic class instead of GWT.create().
-     */
-    this.rf = RequestFactoryMagic.create(DynaTableRequestFactory.class);
-    // Initialization follows the same pattern as client code
-    rf.initialize(new SimpleEventBus(), new HttpClientTransport(uri));
-  }
-
-  /**
-   * Making a request from non-GWT code is similar. The implementation of the
-   * demonstration HttpClientTransport issues the requests synchronously. A
-   * different transport system might use asynchronous callbacks.
-   */
-  private void exec() {
-    rf.schoolCalendarRequest().getPeople(0, 100,
-        Arrays.asList(true, true, true, true, true, true, true)).with("address").fire(
-        new Receiver<List<PersonProxy>>() {
-          @Override
-          public void onSuccess(List<PersonProxy> response) {
-            // Print each record to the console
-            for (PersonProxy person : response) {
-              AddressProxy address = person.getAddress();
-              String addressBlob = address.getStreet() + " "
-                  + address.getCity() + " " + address.getState() + " "
-                  + address.getZip();
-              System.out.printf("%-40s%40s\n%80s\n\n", person.getName(),
-                  person.getDescription(), addressBlob);
-            }
-          }
-        });
-  }
-}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/console/HttpClientTransport.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/console/HttpClientTransport.java
deleted file mode 100644
index 8b5b0c5..0000000
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/console/HttpClientTransport.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2010 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.sample.dynatablerf.console;
-
-import com.google.gwt.dev.util.Util;
-import com.google.gwt.requestfactory.shared.RequestTransport;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-
-/**
- * This is a simple implementation of {@link RequestTransport} that uses
- * HttpClient. It is not suitable for production use, but demonstrates the
- * minimum functionality necessary to implement a custom RequestTransport.
- */
-class HttpClientTransport implements RequestTransport {
-  private final URI uri;
-
-  public HttpClientTransport(URI uri) {
-    this.uri = uri;
-  }
-
-  public void send(String payload, TransportReceiver receiver) {
-    HttpClient client = new DefaultHttpClient();
-    HttpPost post = new HttpPost();
-    post.setHeader("Content-Type", "application/json;charset=UTF-8");
-    post.setURI(uri);
-    Throwable ex;
-    try {
-      post.setEntity(new StringEntity(payload, "UTF-8"));
-      HttpResponse response = client.execute(post);
-      if (200 == response.getStatusLine().getStatusCode()) {
-        String contents = Util.readStreamAsString(response.getEntity().getContent());
-        receiver.onTransportSuccess(contents);
-      } else {
-        receiver.onTransportFailure(response.getStatusLine().getReasonPhrase());
-      }
-      return;
-    } catch (UnsupportedEncodingException e) {
-      ex = e;
-    } catch (ClientProtocolException e) {
-      ex = e;
-    } catch (IOException e) {
-      ex = e;
-    }
-    receiver.onTransportFailure(ex.getMessage());
-  }
-}
\ No newline at end of file
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
index 455e0cf..d4c02c7 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
@@ -23,6 +23,7 @@
 import com.google.gwt.cell.client.NumberCell;
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
@@ -46,7 +47,6 @@
 import com.google.gwt.requestfactory.ui.client.EntityProxyKeyProvider;
 import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.safehtml.client.SafeHtmlTemplates;
-import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
 import com.google.gwt.safehtml.shared.SafeHtml;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
 import com.google.gwt.safehtml.shared.SafeHtmlUtils;
@@ -183,15 +183,16 @@
     }
 
     @Override
-    public boolean isEditing(Element parent, String value, Object key) {
-      return super.isEditing(parent, value, key) || denialPopup.isShowing();
+    public boolean isEditing(Context context, Element parent, String value) {
+      return super.isEditing(context, parent, value) || denialPopup.isShowing();
     }
 
     @Override
-    public void onBrowserEvent(Element parent, String value, Object key,
+    public void onBrowserEvent(Context context, Element parent, String value,
         NativeEvent event, ValueUpdater<String> valueUpdater) {
-      super.onBrowserEvent(parent, value, key, event, valueUpdater);
+      super.onBrowserEvent(context, parent, value, event, valueUpdater);
 
+      Object key = context.getKey();
       String type = event.getType();
       ApprovalViewData viewData = getViewData(key);
       if ("change".equals(type)) {
@@ -235,8 +236,9 @@
     }
 
     @Override
-    public void render(String value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, String value, SafeHtmlBuilder sb) {
       // Get the view data.
+      Object key = context.getKey();
       ApprovalViewData viewData = getViewData(key);
       if (viewData != null && viewData.getPendingApproval().equals(value)) {
         clearViewData(key);
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
index bb40add..2df0dbf 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
@@ -163,7 +163,7 @@
     private static final String replaceString = "<span style='color:red;font-weight:bold;'>$1</span>";
 
     @Override
-    public void render(String value, Object viewData, SafeHtmlBuilder sb) {
+    public void render(Context context, String value, SafeHtmlBuilder sb) {
       if (value != null) {
         if (searchRegExp != null) {
           // The search regex has already been html-escaped
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java
index 49d29be..db78b4d 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseTree.java
@@ -19,13 +19,13 @@
 import com.google.gwt.cell.client.Cell;
 import com.google.gwt.cell.client.IconCellDecorator;
 import com.google.gwt.cell.client.TextCell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Style.Overflow;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.requestfactory.ui.client.EntityProxyKeyProvider;
 import com.google.gwt.safehtml.client.SafeHtmlTemplates;
-import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
 import com.google.gwt.safehtml.shared.SafeHtml;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
 import com.google.gwt.sample.expenses.client.style.Styles;
@@ -85,7 +85,7 @@
         }
 
         @Override
-        public void render(EmployeeProxy value, Object viewData,
+        public void render(Context context, EmployeeProxy value,
             SafeHtmlBuilder sb) {
           if (value != null) {
             StringBuilder classesBuilder = new StringBuilder(usernameStyle);
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java
index df44ef9..db184f7 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseList.java
@@ -81,7 +81,7 @@
     }
 
     @Override
-    public void render(ExpenseProxy value, Object viewData, SafeHtmlBuilder sb) {
+    public void render(Context context, ExpenseProxy value, SafeHtmlBuilder sb) {
       String approval = value.getApproval();
       SafeHtml approvalIcon;
       if (approvedText.equals(approval)) {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java
index 4ef274c..80466d8 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportList.java
@@ -76,8 +76,7 @@
 
     reportList = new CellList<ReportProxy>(new AbstractCell<ReportProxy>() {
       @Override
-      public void render(
-          ReportProxy value, Object viewData, SafeHtmlBuilder sb) {
+      public void render(Context context, ReportProxy value, SafeHtmlBuilder sb) {
         sb.appendHtmlConstant("<div class='item'>");
         sb.appendEscaped(value.getPurpose());
         sb.appendHtmlConstant("</div>");
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SortableHeader.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SortableHeader.java
index 5df24a5..3ab78bf 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SortableHeader.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SortableHeader.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.sample.expenses.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.cell.client.ClickableTextCell;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.resources.client.ClientBundle;
@@ -89,7 +90,7 @@
   }
 
   @Override
-  public void render(SafeHtmlBuilder sb) {
+  public void render(Context context, SafeHtmlBuilder sb) {
     if (sorted) {
       sb.append(template.sorted(IMAGE_WIDTH, reverseSort ? DOWN_ARROW : UP_ARROW, text));
     } else {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Report.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Report.java
index 2975080..96954de 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Report.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/Report.java
@@ -17,11 +17,6 @@
 
 import com.google.appengine.api.datastore.Cursor;
 
-import net.sf.jsr107cache.Cache;
-import net.sf.jsr107cache.CacheException;
-import net.sf.jsr107cache.CacheFactory;
-import net.sf.jsr107cache.CacheManager;
-
 import org.datanucleus.store.appengine.query.JPACursorHelper;
 
 import java.util.Collections;
@@ -29,6 +24,10 @@
 import java.util.List;
 import java.util.logging.Logger;
 
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.CacheFactory;
+import javax.cache.CacheManager;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EntityManager;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java
index 88f90c7..791c63d 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/server/domain/ReportBack.java
@@ -17,11 +17,6 @@
 
 import com.google.appengine.api.datastore.Cursor;
 
-import net.sf.jsr107cache.Cache;
-import net.sf.jsr107cache.CacheException;
-import net.sf.jsr107cache.CacheFactory;
-import net.sf.jsr107cache.CacheManager;
-
 import org.datanucleus.store.appengine.query.JPACursorHelper;
 
 import java.util.Collections;
@@ -29,6 +24,10 @@
 import java.util.List;
 import java.util.logging.Logger;
 
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.CacheFactory;
+import javax.cache.CacheManager;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EntityManager;
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java
index 3c47105..f5f16fc 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java
@@ -109,7 +109,7 @@
    */
   private static class CategoryCell extends AbstractCell<Category> {
     @Override
-    public void render(Category value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, Category value, SafeHtmlBuilder sb) {
       if (value != null) {
         sb.appendEscaped(value.getName());
       }
@@ -121,7 +121,8 @@
    */
   private static class ContentWidgetCell extends AbstractCell<ContentWidget> {
     @Override
-    public void render(ContentWidget value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, ContentWidget value,
+        SafeHtmlBuilder sb) {
       if (value != null) {
         sb.appendEscaped(value.getName());
       }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java
index 3a2248d..b18ff11 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ContactTreeViewModel.java
@@ -69,7 +69,7 @@
     }
 
     @Override
-    public void render(Category value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, Category value, SafeHtmlBuilder sb) {
       if (value != null) {
         sb.appendHtmlConstant(imageHtml).appendEscaped(" ");
         sb.appendEscaped(value.getDisplayName());
@@ -126,7 +126,7 @@
   private static class LetterCountCell extends AbstractCell<LetterCount> {
 
     @Override
-    public void render(LetterCount value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, LetterCount value, SafeHtmlBuilder sb) {
       if (value != null) {
         sb.appendEscaped(value.firstLetter + " (" + value.count + ")");
       }
@@ -193,9 +193,9 @@
     });
     contactCell = new CompositeCell<ContactInfo>(hasCells) {
       @Override
-      public void render(ContactInfo value, Object key, SafeHtmlBuilder sb) {
+      public void render(Context context, ContactInfo value, SafeHtmlBuilder sb) {
         sb.appendHtmlConstant("<table><tbody><tr>");
-        super.render(value, key, sb);
+        super.render(context, value, sb);
         sb.appendHtmlConstant("</tr></tbody></table>");
       }
 
@@ -206,11 +206,11 @@
       }
 
       @Override
-      protected <X> void render(ContactInfo value, Object key,
+      protected <X> void render(Context context, ContactInfo value,
           SafeHtmlBuilder sb, HasCell<ContactInfo, X> hasCell) {
         Cell<X> cell = hasCell.getCell();
         sb.appendHtmlConstant("<td>");
-        cell.render(hasCell.getValue(value), key, sb);
+        cell.render(context, hasCell.getValue(value), sb);
         sb.appendHtmlConstant("</td>");
       }
     };
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java
index 956e31d..4e55af7 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellList.java
@@ -90,7 +90,7 @@
     }
 
     @Override
-    public void render(ContactInfo value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, ContactInfo value, SafeHtmlBuilder sb) {
       // Value can be null, so do a null check..
       if (value == null) {
         return;
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java
index 97c9be7..bd4c872 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/CwCellValidation.java
@@ -85,9 +85,9 @@
     }
 
     @Override
-    public void onBrowserEvent(Element parent, String value, Object key,
+    public void onBrowserEvent(Context context, Element parent, String value,
         NativeEvent event, ValueUpdater<String> valueUpdater) {
-      super.onBrowserEvent(parent, value, key, event, valueUpdater);
+      super.onBrowserEvent(context, parent, value, event, valueUpdater);
 
       // Ignore events that don't target the input.
       Element target = event.getEventTarget().cast();
@@ -95,6 +95,7 @@
         return;
       }
 
+      Object key = context.getKey();
       ValidationData viewData = getViewData(key);
       String eventType = event.getType();
       if ("change".equals(eventType)) {
@@ -120,8 +121,9 @@
     }
 
     @Override
-    public void render(String value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, String value, SafeHtmlBuilder sb) {
       // Get the view data.
+      Object key = context.getKey();
       ValidationData viewData = getViewData(key);
       if (viewData != null && viewData.getValue().equals(value)) {
         // Clear the view data if the value is the same as the current value.
@@ -148,13 +150,13 @@
     }
 
     @Override
-    protected void onEnterKeyDown(Element parent, String value, Object key,
+    protected void onEnterKeyDown(Context context, Element parent, String value,
         NativeEvent event, ValueUpdater<String> valueUpdater) {
       Element target = event.getEventTarget().cast();
       if (getInputElement(parent).isOrHasChild(target)) {
-        finishEditing(parent, value, key, valueUpdater);
+        finishEditing(parent, value, context.getKey(), valueUpdater);
       } else {
-        super.onEnterKeyDown(parent, value, key, event, valueUpdater);
+        super.onEnterKeyDown(context, parent, value, event, valueUpdater);
       }
     }
   }
diff --git a/samples/validation/build.xml b/samples/validation/build.xml
deleted file mode 100755
index 844f146..0000000
--- a/samples/validation/build.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<project name="validation" default="build" basedir=".">
-  <property name="sample.root" value="validation" />
-  <property name="sample.module" value="Validation" />
-  <property name="sample.path" value="src" />
-  <import file="../common.ant.xml" />
-
-  <!-- these are after the common.ant.xml so they have gwt.tools etc. -->
-  <path id="sample.extraclasspath">
-    <pathelement location="${gwt.tools.lib}/apache/log4j/log4j-1.2.16.jar" />
-    <pathelement location="${gwt.tools.lib}/javax/validation/validation-api-1.0.0.GA.jar" />
-    <pathelement location="${gwt.tools.lib}/javax/validation/validation-api-1.0.0.GA-sources.jar" />
-    <pathelement location="${gwt.tools.lib}/hibernate/validator/hibernate-validator-4.1.0.Final.jar" />
-    <pathelement location="${gwt.tools.lib}/hibernate/validator/hibernate-validator-4.1.0.Final-sources.jar" />
-    <pathelement location="${gwt.tools.lib}/slf4j/slf4j-api/slf4j-api-1.6.1.jar" />
-    <pathelement location="${gwt.tools.lib}/slf4j/slf4j-log4j12/slf4j-log4j12-1.6.1.jar" />
-    <!-- Needed for JDK 1.5-->
-    <pathelement location="${gwt.tools.lib}/javax/activation/activation-1.1.jar" />
-    <pathelement location="${gwt.tools.lib}/javax/xml/bind/jaxb-api-2.1.jar" />
-    <pathelement location="${gwt.tools.lib}/sun/jaxb/jaxb-impl-2.1.3.jar" />
-    <pathelement location="${gwt.tools.lib}/javax/xml/stream/stax-api-1.0-2.jar" />
-  </path>
-  <fileset id="sample.server.libs" dir="${gwt.tools.lib}">
-    <include name="javax/validation/validation-api-1.0.0.GA-sources.jar" />
-    <include name="hibernate/validator/hibernate-validator-4.1.0.Final.jar" />
-    <include name="hibernate/validator/hibernate-validator-4.1.0.Final-sources.jar" />
-    <include name="apache/log4j/log4j-1.2.16.jar" />
-    <include name="slf4j/slf4j-api/slf4j-api-1.6.1.jar" />
-    <include name="slf4j/slf4j-log4j12/slf4j-log4j12-1.6.1.jar" />
-  </fileset>
-</project>
diff --git a/tools/api-checker/config/gwt20_21userApi.conf b/tools/api-checker/config/gwt20_21userApi.conf
index 697b3cc..54313db 100644
--- a/tools/api-checker/config/gwt20_21userApi.conf
+++ b/tools/api-checker/config/gwt20_21userApi.conf
@@ -66,6 +66,7 @@
 :**/server/**\
 :**/tools/**\
 :user/src/com/google/gwt/regexp/shared/**\
+:user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java\
 :user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java\
 :user/src/com/google/gwt/core/client/impl/WeakMapping.java\
 :user/src/com/google/gwt/junit/*.java\
diff --git a/tools/api-checker/config/gwt21_22userApi.conf b/tools/api-checker/config/gwt21_22userApi.conf
index 405ee8d..3e4f820 100644
--- a/tools/api-checker/config/gwt21_22userApi.conf
+++ b/tools/api-checker/config/gwt21_22userApi.conf
@@ -73,6 +73,7 @@
 :**/server/**\
 :**/tools/**\
 :user/src/com/google/gwt/regexp/shared/**\
+:user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java\
 :user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java\
 :user/src/com/google/gwt/core/client/impl/WeakMapping.java\
 :user/src/com/google/gwt/junit/*.java\
@@ -143,3 +144,124 @@
 com.google.gwt.user.cellview.client.Column::cell MISSING
 com.google.gwt.user.cellview.client.Column::fieldUpdater MISSING
 
+# Passing a Context to Cell methods instead of passing a bunch of different args.
+com.google.gwt.cell.client.AbstractCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.AbstractCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.AbstractCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.AbstractCell::render(Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.AbstractCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.AbstractCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.AbstractEditableCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.AbstractInputCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.AbstractInputCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.AbstractInputCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.AbstractInputCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.AbstractSafeHtmlCell::render(Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.AbstractSafeHtmlCell::render(Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ActionCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ActionCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ActionCell::render(Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ButtonCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ButtonCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ButtonCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ButtonCell::render(Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ButtonCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ButtonCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ButtonCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.CheckboxCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/Boolean;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.CheckboxCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Boolean;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.CheckboxCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/Boolean;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.CheckboxCell::render(Ljava/lang/Boolean;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.CheckboxCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/Boolean;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.CheckboxCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/Boolean;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::render(Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ClickableTextCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.CompositeCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.CompositeCell::render(Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.CompositeCell::render(Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;Lcom/google/gwt/cell/client/HasCell;) MISSING
+com.google.gwt.cell.client.CompositeCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.CompositeCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.DateCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.DateCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.DateCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.DateCell::render(Ljava/util/Date;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.DateCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.DateCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.DatePickerCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.DatePickerCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.DatePickerCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.DatePickerCell::render(Ljava/util/Date;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.DatePickerCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.DatePickerCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/util/Date;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.EditTextCell::edit(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.EditTextCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.EditTextCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.EditTextCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.EditTextCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.EditTextCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.EditTextCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.IconCellDecorator::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.IconCellDecorator::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.IconCellDecorator::render(Ljava/lang/Object;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.IconCellDecorator::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.IconCellDecorator::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ImageCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ImageCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ImageCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageLoadingCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageLoadingCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ImageLoadingCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ImageLoadingCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ImageLoadingCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageLoadingCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageResourceCell::isEditing(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/resources/client/ImageResource;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageResourceCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/resources/client/ImageResource;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ImageResourceCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/resources/client/ImageResource;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.ImageResourceCell::render(Lcom/google/gwt/resources/client/ImageResource;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.ImageResourceCell::resetFocus(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/resources/client/ImageResource;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.ImageResourceCell::setValue(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/resources/client/ImageResource;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.NumberCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/Number;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.NumberCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/Number;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.NumberCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/Number;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.NumberCell::render(Ljava/lang/Number;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.NumberCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/Number;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.NumberCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/Number;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.SafeHtmlCell::isEditing(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.SafeHtmlCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.SafeHtmlCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.SafeHtmlCell::render(Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.SafeHtmlCell::resetFocus(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.SafeHtmlCell::setValue(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.SelectionCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.SelectionCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.SelectionCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.SelectionCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.SelectionCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.SelectionCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.TextCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.TextCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.TextCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.TextCell::render(Lcom/google/gwt/safehtml/shared/SafeHtml;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.TextCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.TextCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.TextCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.TextInputCell::isEditing(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.TextInputCell::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.TextInputCell::onEnterKeyDown(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/cell/client/ValueUpdater;) MISSING
+com.google.gwt.cell.client.TextInputCell::render(Ljava/lang/String;Ljava/lang/Object;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.cell.client.TextInputCell::resetFocus(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.cell.client.TextInputCell::setValue(Lcom/google/gwt/dom/client/Element;Ljava/lang/String;Ljava/lang/Object;) MISSING
+com.google.gwt.user.cellview.client.CellList::fireEventToCell(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/dom/client/Element;Ljava/lang/Object;) MISSING
+com.google.gwt.user.cellview.client.Column::render(Ljava/lang/Object;Lcom/google/gwt/view/client/ProvidesKey;Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+com.google.gwt.user.cellview.client.Column::onBrowserEvent(Lcom/google/gwt/dom/client/Element;ILjava/lang/Object;Lcom/google/gwt/dom/client/NativeEvent;Lcom/google/gwt/view/client/ProvidesKey;) MISSING
+com.google.gwt.user.cellview.client.Header::onBrowserEvent(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/NativeEvent;) MISSING
+com.google.gwt.user.cellview.client.Header::render(Lcom/google/gwt/safehtml/shared/SafeHtmlBuilder;) MISSING
+
diff --git a/user/javadoc/com/google/gwt/examples/cell/CellExample.java b/user/javadoc/com/google/gwt/examples/cell/CellExample.java
index 2c8317c..524889d 100644
--- a/user/javadoc/com/google/gwt/examples/cell/CellExample.java
+++ b/user/javadoc/com/google/gwt/examples/cell/CellExample.java
@@ -39,7 +39,7 @@
   private static class ColorCell extends AbstractCell<String> {
 
     @Override
-    public void render(String value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, String value, SafeHtmlBuilder sb) {
       /*
        * Always do a null check on the value. Cell widgets can pass null to
        * cells if the underlying data contains a null, or if the data arrives
diff --git a/user/javadoc/com/google/gwt/examples/cell/EditableCellExample.java b/user/javadoc/com/google/gwt/examples/cell/EditableCellExample.java
index 024a01b..73aacf2 100644
--- a/user/javadoc/com/google/gwt/examples/cell/EditableCellExample.java
+++ b/user/javadoc/com/google/gwt/examples/cell/EditableCellExample.java
@@ -67,7 +67,7 @@
     }
 
     @Override
-    public void onBrowserEvent(Element parent, Contact value, Object key,
+    public void onBrowserEvent(Context context, Element parent, Contact value,
         NativeEvent event, ValueUpdater<Contact> valueUpdater) {
       // Check that the value is not null.
       if (value == null) {
@@ -75,7 +75,7 @@
       }
 
       // Call the super handler, which handlers the enter key.
-      super.onBrowserEvent(parent, value, key, event, valueUpdater);
+      super.onBrowserEvent(context, parent, value, event, valueUpdater);
 
       // Handle click events.
       if ("change".equals(event.getType())) {
@@ -85,7 +85,7 @@
     }
 
     @Override
-    public void render(Contact value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, Contact value, SafeHtmlBuilder sb) {
       /*
        * Always do a null check on the value. Cell widgets can pass null to
        * cells if the underlying data contains a null, or if the data arrives
@@ -123,8 +123,8 @@
      * navigation in the widget. Our cell will toggle the checkbox on Enter.
      */
     @Override
-    protected void onEnterKeyDown(Element parent, Contact value, Object key,
-        NativeEvent event, ValueUpdater<Contact> valueUpdater) {
+    protected void onEnterKeyDown(Context context, Element parent,
+        Contact value, NativeEvent event, ValueUpdater<Contact> valueUpdater) {
       // Toggle the checkbox.
       InputElement input = getInputElement(parent);
       input.setChecked(!input.isChecked());
diff --git a/user/javadoc/com/google/gwt/examples/cell/InteractionCellExample.java b/user/javadoc/com/google/gwt/examples/cell/InteractionCellExample.java
index ceaa7a3..70c7001 100644
--- a/user/javadoc/com/google/gwt/examples/cell/InteractionCellExample.java
+++ b/user/javadoc/com/google/gwt/examples/cell/InteractionCellExample.java
@@ -78,7 +78,7 @@
     }
 
     @Override
-    public void onBrowserEvent(Element parent, Contact value, Object key,
+    public void onBrowserEvent(Context context, Element parent, Contact value,
         NativeEvent event, ValueUpdater<Contact> valueUpdater) {
       // Check that the value is not null.
       if (value == null) {
@@ -86,16 +86,16 @@
       }
 
       // Call the super handler, which handlers the enter key.
-      super.onBrowserEvent(parent, value, key, event, valueUpdater);
+      super.onBrowserEvent(context, parent, value, event, valueUpdater);
 
       // On click, perform the same action that we perform on enter.
       if ("click".equals(event.getType())) {
-        this.onEnterKeyDown(parent, value, key, event, valueUpdater);
+        this.onEnterKeyDown(context, parent, value, event, valueUpdater);
       }
     }
 
     @Override
-    public void render(Contact value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, Contact value, SafeHtmlBuilder sb) {
       /*
        * Always do a null check on the value. Cell widgets can pass null to
        * cells if the underlying data contains a null, or if the data arrives
@@ -128,8 +128,8 @@
      * navigation in the widget.
      */
     @Override
-    protected void onEnterKeyDown(Element parent, Contact value, Object key,
-        NativeEvent event, ValueUpdater<Contact> valueUpdater) {
+    protected void onEnterKeyDown(Context context, Element parent,
+        Contact value, NativeEvent event, ValueUpdater<Contact> valueUpdater) {
       Window.alert("You clicked " + value.name);
     }
   }
diff --git a/user/javadoc/com/google/gwt/examples/cellview/CellBrowserExample2.java b/user/javadoc/com/google/gwt/examples/cellview/CellBrowserExample2.java
index 013db42..5aa5173 100644
--- a/user/javadoc/com/google/gwt/examples/cellview/CellBrowserExample2.java
+++ b/user/javadoc/com/google/gwt/examples/cellview/CellBrowserExample2.java
@@ -209,7 +209,7 @@
         // Create a cell to display a composer.
         Cell<Composer> cell = new AbstractCell<Composer>() {
           @Override
-          public void render(Composer value, Object key, SafeHtmlBuilder sb) {
+          public void render(Context context, Composer value, SafeHtmlBuilder sb) {
             if (value != null) {
               sb.appendEscaped(value.getName());
             }
@@ -225,7 +225,7 @@
             ((Composer) value).getPlaylists());
         Cell<Playlist> cell = new AbstractCell<Playlist>() {
           @Override
-          public void render(Playlist value, Object key, SafeHtmlBuilder sb) {
+          public void render(Context context, Playlist value, SafeHtmlBuilder sb) {
             if (value != null) {
               sb.appendEscaped(value.getName());
             }
diff --git a/user/javadoc/com/google/gwt/examples/cellview/CellTreeExample2.java b/user/javadoc/com/google/gwt/examples/cellview/CellTreeExample2.java
index 6c713f8..6825e76 100644
--- a/user/javadoc/com/google/gwt/examples/cellview/CellTreeExample2.java
+++ b/user/javadoc/com/google/gwt/examples/cellview/CellTreeExample2.java
@@ -210,7 +210,7 @@
         // Create a cell to display a composer.
         Cell<Composer> cell = new AbstractCell<Composer>() {
           @Override
-          public void render(Composer value, Object key, SafeHtmlBuilder sb) {
+          public void render(Context context, Composer value, SafeHtmlBuilder sb) {
             if (value != null) {
               sb.appendEscaped(value.getName());
             }
@@ -226,7 +226,7 @@
             ((Composer) value).getPlaylists());
         Cell<Playlist> cell = new AbstractCell<Playlist>() {
           @Override
-          public void render(Playlist value, Object key, SafeHtmlBuilder sb) {
+          public void render(Context context, Playlist value, SafeHtmlBuilder sb) {
             if (value != null) {
               sb.appendEscaped(value.getName());
             }
diff --git a/user/javadoc/com/google/gwt/examples/view/KeyProviderExample.java b/user/javadoc/com/google/gwt/examples/view/KeyProviderExample.java
index f3cfc89..434019e 100644
--- a/user/javadoc/com/google/gwt/examples/view/KeyProviderExample.java
+++ b/user/javadoc/com/google/gwt/examples/view/KeyProviderExample.java
@@ -54,7 +54,7 @@
    */
   private static class ContactCell extends AbstractCell<Contact> {
     @Override
-    public void render(Contact value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, Contact value, SafeHtmlBuilder sb) {
       if (value != null) {
         sb.appendEscaped(value.name);
       }
diff --git a/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java b/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java
index ac2653b..315e185 100644
--- a/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java
+++ b/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java
@@ -23,6 +23,7 @@
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * A base type to handle analyzing the return value of a getter method. The
@@ -30,47 +31,61 @@
  */
 abstract class MethodPropertyContext implements CollectionPropertyContext,
     MapPropertyContext {
-  private final Class<?> keyType;
-  private final Class<?> valueType;
-  private final Class<?> elementType;
-  private final Class<?> type;
+  private static class Data {
+    Class<?> keyType;
+    Class<?> valueType;
+    Class<?> elementType;
+    Class<?> type;
+  }
+
+  /**
+   * Save prior instances in order to decrease the amount of data computed.
+   */
+  private static final Map<Method, Data> cache = new WeakHashMap<Method, Data>();
+
+  private final Data data;
 
   public MethodPropertyContext(Method getter) {
-    this.type = getter.getReturnType();
+    synchronized (cache) {
+      Data previous = cache.get(getter);
+      if (previous != null) {
+        this.data = previous;
+        return;
+      }
 
-    // Compute collection element type
-    if (Collection.class.isAssignableFrom(getType())) {
-      elementType = TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(
-          Collection.class, getter.getGenericReturnType(),
-          getter.getReturnType()));
-      keyType = valueType = null;
-    } else if (Map.class.isAssignableFrom(getType())) {
-      Type[] types = TypeUtils.getParameterization(Map.class,
-          getter.getGenericReturnType());
-      keyType = TypeUtils.ensureBaseType(types[0]);
-      valueType = TypeUtils.ensureBaseType(types[1]);
-      elementType = null;
-    } else {
-      elementType = keyType = valueType = null;
+      this.data = new Data();
+      data.type = getter.getReturnType();
+      // Compute collection element type
+      if (Collection.class.isAssignableFrom(getType())) {
+        data.elementType = TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(
+            Collection.class, getter.getGenericReturnType(),
+            getter.getReturnType()));
+      } else if (Map.class.isAssignableFrom(getType())) {
+        Type[] types = TypeUtils.getParameterization(Map.class,
+            getter.getGenericReturnType());
+        data.keyType = TypeUtils.ensureBaseType(types[0]);
+        data.valueType = TypeUtils.ensureBaseType(types[1]);
+      }
+      cache.put(getter, data);
     }
   }
 
   public abstract boolean canSet();
 
   public Class<?> getElementType() {
-    return elementType;
+    return data.elementType;
   }
 
   public Class<?> getKeyType() {
-    return keyType;
+    return data.keyType;
   }
 
   public Class<?> getType() {
-    return type;
+    return data.type;
   }
 
   public Class<?> getValueType() {
-    return valueType;
+    return data.valueType;
   }
 
   public abstract void set(Object value);
diff --git a/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java b/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
index 7c4772d..0992adc 100644
--- a/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
+++ b/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
@@ -28,9 +28,10 @@
 import java.lang.reflect.Proxy;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * An implementation of an AutoBean that uses reflection.
@@ -38,9 +39,60 @@
  * @param <T> the type of interface being wrapped
  */
 class ProxyAutoBean<T> extends AbstractAutoBean<T> {
+  private static class Data {
+    final List<Method> getters = new ArrayList<Method>();
+    final List<String> getterNames = new ArrayList<String>();
+    final List<PropertyType> propertyType = new ArrayList<PropertyType>();
+  }
+
+  private enum PropertyType {
+    VALUE, REFERENCE, COLLECTION, MAP;
+  }
+
+  private static final Map<Class<?>, Data> cache = new WeakHashMap<Class<?>, Data>();
+
+  private static Data calculateData(Class<?> beanType) {
+    Data toReturn;
+    synchronized (cache) {
+      toReturn = cache.get(beanType);
+      if (toReturn == null) {
+        toReturn = new Data();
+        for (Method method : beanType.getMethods()) {
+          if (BeanMethod.GET.matches(method)) {
+            toReturn.getters.add(method);
+
+            String name;
+            PropertyName annotation = method.getAnnotation(PropertyName.class);
+            if (annotation != null) {
+              name = annotation.value();
+            } else {
+              name = method.getName();
+              name = Character.toLowerCase(name.charAt(3))
+                  + (name.length() >= 5 ? name.substring(4) : "");
+            }
+            toReturn.getterNames.add(name);
+
+            Class<?> returnType = method.getReturnType();
+            if (TypeUtils.isValueType(returnType)) {
+              toReturn.propertyType.add(PropertyType.VALUE);
+            } else if (Collection.class.isAssignableFrom(returnType)) {
+              toReturn.propertyType.add(PropertyType.COLLECTION);
+            } else if (Map.class.isAssignableFrom(returnType)) {
+              toReturn.propertyType.add(PropertyType.MAP);
+            } else {
+              toReturn.propertyType.add(PropertyType.REFERENCE);
+            }
+          }
+        }
+        cache.put(beanType, toReturn);
+      }
+    }
+    return toReturn;
+  }
+
   private final Class<T> beanType;
   private final Configuration configuration;
-  private final List<Method> getters;
+  private final Data data;
   private final T shim;
 
   // These constructors mirror the generated constructors.
@@ -50,7 +102,7 @@
     super(factory);
     this.beanType = (Class<T>) beanType;
     this.configuration = configuration;
-    this.getters = calculateGetters();
+    this.data = calculateData(beanType);
     this.shim = createShim();
   }
 
@@ -63,7 +115,7 @@
     }
     this.beanType = (Class<T>) beanType;
     this.configuration = configuration;
-    this.getters = calculateGetters();
+    this.data = calculateData(beanType);
     this.shim = createShim();
   }
 
@@ -71,7 +123,7 @@
     super(toClone, deep);
     this.beanType = toClone.beanType;
     this.configuration = toClone.configuration;
-    this.getters = toClone.getters;
+    this.data = toClone.data;
     this.shim = createShim();
   }
 
@@ -156,16 +208,15 @@
   // TODO: Port to model-based when class-based TypeOracle is available.
   @Override
   protected void traverseProperties(AutoBeanVisitor visitor, OneShotContext ctx) {
-    for (final Method getter : getters) {
-      String name;
-      PropertyName annotation = getter.getAnnotation(PropertyName.class);
-      if (annotation != null) {
-        name = annotation.value();
-      } else {
-        name = getter.getName();
-        name = Character.toLowerCase(name.charAt(3))
-            + (name.length() >= 5 ? name.substring(4) : "");
-      }
+    assert data.getters.size() == data.getterNames.size()
+        && data.getters.size() == data.propertyType.size();
+    Iterator<Method> getterIt = data.getters.iterator();
+    Iterator<String> nameIt = data.getterNames.iterator();
+    Iterator<PropertyType> typeIt = data.propertyType.iterator();
+    while (getterIt.hasNext()) {
+      Method getter = getterIt.next();
+      String name = nameIt.next();
+      PropertyType propertyType = typeIt.next();
 
       // Use the shim to handle automatic wrapping
       Object value;
@@ -184,42 +235,51 @@
       MethodPropertyContext x = isUsingSimplePeer() ? new BeanPropertyContext(
           this, getter) : new GetterPropertyContext(this, getter);
 
-      if (TypeUtils.isValueType(x.getType())) {
-        if (visitor.visitValueProperty(name, value, x)) {
-        }
-        visitor.endVisitValueProperty(name, value, x);
-      } else if (Collection.class.isAssignableFrom(x.getType())) {
-        // Workaround for generics bug in mac javac 1.6.0_22
-        @SuppressWarnings("rawtypes")
-        AutoBean temp = AutoBeanUtils.getAutoBean((Collection) value);
-        @SuppressWarnings("unchecked")
-        AutoBean<Collection<?>> bean = (AutoBean<Collection<?>>) temp;
-        if (visitor.visitCollectionProperty(name, bean, x)) {
-          if (value != null) {
-            ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+      switch (propertyType) {
+        case VALUE: {
+          if (visitor.visitValueProperty(name, value, x)) {
           }
+          visitor.endVisitValueProperty(name, value, x);
+          break;
         }
-        visitor.endVisitCollectionProperty(name, bean, x);
-      } else if (Map.class.isAssignableFrom(x.getType())) {
-        // Workaround for generics bug in mac javac 1.6.0_22
-        @SuppressWarnings("rawtypes")
-        AutoBean temp = AutoBeanUtils.getAutoBean((Map) value);
-        @SuppressWarnings("unchecked")
-        AutoBean<Map<?, ?>> bean = (AutoBean<Map<?, ?>>) temp;
-        if (visitor.visitMapProperty(name, bean, x)) {
-          if (value != null) {
-            ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+        case COLLECTION: {
+          // Workaround for generics bug in mac javac 1.6.0_22
+          @SuppressWarnings("rawtypes")
+          AutoBean temp = AutoBeanUtils.getAutoBean((Collection) value);
+          @SuppressWarnings("unchecked")
+          AutoBean<Collection<?>> bean = (AutoBean<Collection<?>>) temp;
+          if (visitor.visitCollectionProperty(name, bean, x)) {
+            if (value != null) {
+              ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+            }
           }
+          visitor.endVisitCollectionProperty(name, bean, x);
+          break;
         }
-        visitor.endVisitMapProperty(name, bean, x);
-      } else {
-        ProxyAutoBean<?> bean = (ProxyAutoBean<?>) AutoBeanUtils.getAutoBean(value);
-        if (visitor.visitReferenceProperty(name, bean, x)) {
-          if (value != null) {
-            bean.traverse(visitor, ctx);
+        case MAP: {
+          // Workaround for generics bug in mac javac 1.6.0_22
+          @SuppressWarnings("rawtypes")
+          AutoBean temp = AutoBeanUtils.getAutoBean((Map) value);
+          @SuppressWarnings("unchecked")
+          AutoBean<Map<?, ?>> bean = (AutoBean<Map<?, ?>>) temp;
+          if (visitor.visitMapProperty(name, bean, x)) {
+            if (value != null) {
+              ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+            }
           }
+          visitor.endVisitMapProperty(name, bean, x);
+          break;
         }
-        visitor.endVisitReferenceProperty(name, bean, x);
+        case REFERENCE: {
+          ProxyAutoBean<?> bean = (ProxyAutoBean<?>) AutoBeanUtils.getAutoBean(value);
+          if (visitor.visitReferenceProperty(name, bean, x)) {
+            if (value != null) {
+              bean.traverse(visitor, ctx);
+            }
+          }
+          visitor.endVisitReferenceProperty(name, bean, x);
+          break;
+        }
       }
     }
   }
@@ -232,16 +292,6 @@
     return values;
   }
 
-  private List<Method> calculateGetters() {
-    List<Method> toReturn = new ArrayList<Method>();
-    for (Method method : beanType.getMethods()) {
-      if (BeanMethod.GET.matches(method)) {
-        toReturn.add(method);
-      }
-    }
-    return Collections.unmodifiableList(toReturn);
-  }
-
   private T createShim() {
     T toReturn = AutoBeanFactoryMagic.makeProxy(beanType, new ShimHandler<T>(
         this, getWrapped()));
diff --git a/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java b/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java
index 9d228f4..2ae2f0e 100644
--- a/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java
+++ b/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java
@@ -295,7 +295,8 @@
 
       if (ValueCodex.canDecode(ctx.getElementType())) {
         for (Object element : collection) {
-          sb.append(",").append(encodeValue(element).getPayload());
+          sb.append(",").append(
+              encodeValue(ctx.getElementType(), element).getPayload());
         }
       } else {
         boolean isEncoded = Splittable.class.equals(ctx.getElementType());
@@ -330,15 +331,18 @@
         return false;
       }
 
-      boolean isEncodedKey = Splittable.class.equals(ctx.getKeyType());
-      boolean isEncodedValue = Splittable.class.equals(ctx.getValueType());
-      boolean isValueKey = ValueCodex.canDecode(ctx.getKeyType());
-      boolean isValueValue = ValueCodex.canDecode(ctx.getValueType());
+      Class<?> keyType = ctx.getKeyType();
+      Class<?> valueType = ctx.getValueType();
+      boolean isEncodedKey = Splittable.class.equals(keyType);
+      boolean isEncodedValue = Splittable.class.equals(valueType);
+      boolean isValueKey = ValueCodex.canDecode(keyType);
+      boolean isValueValue = ValueCodex.canDecode(valueType);
 
       if (isValueKey) {
-        writeValueKeyMap(map, isEncodedValue, isValueValue);
+        writeValueKeyMap(map, keyType, valueType, isEncodedValue, isValueValue);
       } else {
-        writeObjectKeyMap(map, isEncodedKey, isEncodedValue, isValueValue);
+        writeObjectKeyMap(map, valueType, isEncodedKey, isEncodedValue,
+            isValueValue);
       }
 
       return false;
@@ -377,7 +381,7 @@
 
       // Special handling for enums if we have an obfuscation map
       Splittable split;
-      split = encodeValue(value);
+      split = encodeValue(type, value);
       sb.append(",\"").append(propertyName).append("\":").append(
           split.getPayload());
       return false;
@@ -409,12 +413,13 @@
      * Encodes a value, with special handling for enums to allow the field name
      * to be overridden.
      */
-    private Splittable encodeValue(Object value) {
+    private Splittable encodeValue(Class<?> expectedType, Object value) {
       Splittable split;
       if (value instanceof Enum<?> && enumMap != null) {
-        split = ValueCodex.encode(enumMap.getToken((Enum<?>) value));
+        split = ValueCodex.encode(String.class,
+            enumMap.getToken((Enum<?>) value));
       } else {
-        split = ValueCodex.encode(value);
+        split = ValueCodex.encode(expectedType, value);
       }
       return split;
     }
@@ -429,8 +434,8 @@
      * encoded as a list of two lists, since it's possible that two distinct
      * objects have the same encoded form.
      */
-    private void writeObjectKeyMap(Map<?, ?> map, boolean isEncodedKey,
-        boolean isEncodedValue, boolean isValueValue) {
+    private void writeObjectKeyMap(Map<?, ?> map, Class<?> valueType,
+        boolean isEncodedKey, boolean isEncodedValue, boolean isValueValue) {
       StringBuilder keys = new StringBuilder();
       StringBuilder values = new StringBuilder();
 
@@ -445,7 +450,8 @@
           values.append(",").append(
               ((Splittable) entry.getValue()).getPayload());
         } else if (isValueValue) {
-          values.append(",").append(encodeValue(entry.getValue()).getPayload());
+          values.append(",").append(
+              encodeValue(valueType, entry.getValue()).getPayload());
         } else {
           encodeToStringBuilder(values.append(","), entry.getValue());
         }
@@ -462,15 +468,15 @@
     /**
      * Writes a map JSON literal where the keys are value types.
      */
-    private void writeValueKeyMap(Map<?, ?> map, boolean isEncodedValue,
-        boolean isValueValue) {
+    private void writeValueKeyMap(Map<?, ?> map, Class<?> keyType,
+        Class<?> valueType, boolean isEncodedValue, boolean isValueValue) {
       for (Map.Entry<?, ?> entry : map.entrySet()) {
-        sb.append(",").append(encodeValue(entry.getKey()).getPayload()).append(
+        sb.append(",").append(encodeValue(keyType, entry.getKey()).getPayload()).append(
             ":");
         if (isEncodedValue) {
           sb.append(((Splittable) entry.getValue()).getPayload());
         } else if (isValueValue) {
-          sb.append(encodeValue(entry.getValue()).getPayload());
+          sb.append(encodeValue(valueType, entry.getValue()).getPayload());
         } else {
           encodeToStringBuilder(sb, entry.getValue());
         }
diff --git a/user/src/com/google/gwt/autobean/shared/ValueCodex.java b/user/src/com/google/gwt/autobean/shared/ValueCodex.java
index 19ef456..ca867eb 100644
--- a/user/src/com/google/gwt/autobean/shared/ValueCodex.java
+++ b/user/src/com/google/gwt/autobean/shared/ValueCodex.java
@@ -20,9 +20,11 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Provides unified encoding and decoding of value objects.
@@ -31,6 +33,11 @@
   enum Type {
     BIG_DECIMAL(BigDecimal.class) {
       @Override
+      public boolean canUpcast(Object value) {
+        return value instanceof BigDecimal;
+      }
+
+      @Override
       public BigDecimal decode(Class<?> clazz, String value) {
         return new BigDecimal(value);
       }
@@ -42,6 +49,11 @@
     },
     BIG_INTEGER(BigInteger.class) {
       @Override
+      public boolean canUpcast(Object value) {
+        return value instanceof BigInteger;
+      }
+
+      @Override
       public BigInteger decode(Class<?> clazz, String value) {
         return new BigInteger(value);
       }
@@ -71,6 +83,11 @@
     },
     DATE(Date.class) {
       @Override
+      public boolean canUpcast(Object value) {
+        return value instanceof Date;
+      }
+
+      @Override
       public Date decode(Class<?> clazz, String value) {
         return new Date(Long.valueOf(value));
       }
@@ -158,6 +175,15 @@
       this.defaultValue = defaultValue;
     }
 
+    /**
+     * Determines whether or not the Type can handle the given value via
+     * upcasting semantics.
+     */
+    public boolean canUpcast(Object value) {
+      // Most value types are final, so this method is meaningless
+      return false;
+    }
+
     public abstract Object decode(Class<?> clazz, String value);
 
     public Object getDefaultValue() {
@@ -177,14 +203,18 @@
     }
   }
 
-  private static Map<Class<?>, Type> typesByClass = new HashMap<Class<?>, Type>();
+  private static final Set<Class<?>> ALL_VALUE_TYPES;
+  private static final Map<Class<?>, Type> TYPES_BY_CLASS;
   static {
+    Map<Class<?>, Type> temp = new HashMap<Class<?>, Type>();
     for (Type t : Type.values()) {
-      typesByClass.put(t.getType(), t);
+      temp.put(t.getType(), t);
       if (t.getPrimitiveType() != null) {
-        typesByClass.put(t.getPrimitiveType(), t);
+        temp.put(t.getPrimitiveType(), t);
       }
     }
+    ALL_VALUE_TYPES = Collections.unmodifiableSet(temp.keySet());
+    TYPES_BY_CLASS = Collections.unmodifiableMap(temp);
   }
 
   /**
@@ -194,7 +224,11 @@
    * @return {@code true} if the given object type can be decoded
    */
   public static boolean canDecode(Class<?> clazz) {
-    return findType(clazz) != null;
+    if (findType(clazz) != null) {
+      return true;
+    }
+    // Use other platform-specific tests
+    return ValueCodexHelper.canDecode(clazz);
   }
 
   public static <T> T decode(Class<T> clazz, Splittable split) {
@@ -212,12 +246,42 @@
     return (T) getTypeOrDie(clazz).decode(clazz, string);
   }
 
+  /**
+   * Encode a value object when the wire format type is known. This method
+   * should be preferred over {@link #encode(Object)} when possible.
+   */
+  public static Splittable encode(Class<?> clazz, Object obj) {
+    if (obj == null) {
+      return LazySplittable.NULL;
+    }
+    return new LazySplittable(getTypeOrDie(clazz).toJsonExpression(obj));
+  }
+
   public static Splittable encode(Object obj) {
     if (obj == null) {
       return LazySplittable.NULL;
     }
-    return new LazySplittable(
-        getTypeOrDie(obj.getClass()).toJsonExpression(obj));
+    Type t = findType(obj.getClass());
+    // Try upcasting
+    if (t == null) {
+      for (Type maybe : Type.values()) {
+        if (maybe.canUpcast(obj)) {
+          t = maybe;
+          break;
+        }
+      }
+    }
+    if (t == null) {
+      throw new UnsupportedOperationException(obj.getClass().getName());
+    }
+    return new LazySplittable(t.toJsonExpression(obj));
+  }
+
+  /**
+   * Return all Value types that can be processed by the ValueCodex.
+   */
+  public static Set<Class<?>> getAllValueTypes() {
+    return ALL_VALUE_TYPES;
   }
 
   /**
@@ -235,14 +299,10 @@
    * May return <code>null</code>.
    */
   private static <T> Type findType(Class<T> clazz) {
-    Type type = typesByClass.get(clazz);
-    if (type == null) {
-      // Necessary due to lack of Class.isAssignable() in client-side
-      if (clazz.getEnumConstants() != null) {
-        return Type.ENUM;
-      }
+    if (clazz.isEnum()) {
+      return Type.ENUM;
     }
-    return type;
+    return TYPES_BY_CLASS.get(clazz);
   }
 
   private static <T> Type getTypeOrDie(Class<T> clazz) {
diff --git a/user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java b/user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java
new file mode 100644
index 0000000..c6f72c8
--- /dev/null
+++ b/user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 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.autobean.shared;
+
+import com.google.gwt.core.client.GWT;
+
+/**
+ * Provides reflection-based operation for server (JVM) implementation. There is
+ * a no-op super-source version for client (dev- and web-mode) code.
+ */
+class ValueCodexHelper {
+  /**
+   * Returns {@code true} if {@code clazz} is assignable to any of the value
+   * types.
+   */
+  static boolean canDecode(Class<?> clazz) {
+    assert !GWT.isClient();
+    for (Class<?> valueType : ValueCodex.getAllValueTypes()) {
+      if (valueType.isAssignableFrom(clazz)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java b/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java
index 9f65e5e..15ce275 100644
--- a/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java
+++ b/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java
@@ -17,7 +17,8 @@
 
 import com.google.gwt.autobean.server.impl.JsonSplittable;
 import com.google.gwt.autobean.shared.Splittable;
-import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
+
+import org.json.JSONObject;
 
 /**
  * This class has a super-source version with a client-only implementation.
@@ -27,7 +28,7 @@
    * Create a quoted JSON string.
    */
   public static String quote(String raw) {
-    return ServerSerializationStreamWriter.escapeString(raw);
+    return JSONObject.quote(raw);
   }
 
   public static Splittable split(String payload) {
diff --git a/user/src/com/google/gwt/cell/client/AbstractCell.java b/user/src/com/google/gwt/cell/client/AbstractCell.java
index d47edee..e98f6af 100644
--- a/user/src/com/google/gwt/cell/client/AbstractCell.java
+++ b/user/src/com/google/gwt/cell/client/AbstractCell.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.event.dom.client.KeyCodes;
@@ -26,7 +27,7 @@
 
 /**
  * A default implementation of the {@link Cell} interface.
- *
+ * 
  * <p>
  * <h3>Examples</h3>
  * <dl>
@@ -38,7 +39,7 @@
  * <dd>{@example com.google.gwt.examples.cell.EditableCellExample}</dd>
  * </dl>
  * </p>
- *
+ * 
  * @param <C> the type that this Cell represents
  */
 public abstract class AbstractCell<C> implements Cell<C> {
@@ -51,7 +52,7 @@
   /**
    * Construct a new {@link AbstractCell} with the specified consumed events.
    * The input arguments are passed by copy.
-   *
+   * 
    * @param consumedEvents the events that this cell consumes
    */
   public AbstractCell(String... consumedEvents) {
@@ -67,7 +68,7 @@
 
   /**
    * Construct a new {@link AbstractCell} with the specified consumed events.
-   *
+   * 
    * @param consumedEvents the events that this cell consumes
    */
   public AbstractCell(Set<String> consumedEvents) {
@@ -90,45 +91,45 @@
    * Returns false. Subclasses that support editing should override this method
    * to return the current editing status.
    */
-  public boolean isEditing(Element parent, C value, Object key) {
+  public boolean isEditing(Context context, Element parent, C value) {
     return false;
   }
 
   /**
    * {@inheritDoc}
-   *
+   * 
    * <p>
    * If you override this method to add support for events, remember to pass the
    * event types that the cell expects into the constructor.
    * </p>
    */
-  public void onBrowserEvent(Element parent, C value, Object key,
+  public void onBrowserEvent(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
     String eventType = event.getType();
     // Special case the ENTER key for a unified user experience.
     if ("keydown".equals(eventType) && event.getKeyCode() == KeyCodes.KEY_ENTER) {
-      onEnterKeyDown(parent, value, key, event, valueUpdater);
+      onEnterKeyDown(context, parent, value, event, valueUpdater);
     }
   }
 
-  public abstract void render(C value, Object key, SafeHtmlBuilder sb);
+  public abstract void render(Context context, C value, SafeHtmlBuilder sb);
 
   /**
    * {@inheritDoc}
-   *
+   * 
    * <p>
    * This method is a no-op and returns false. If your cell is editable or can
    * be focused by the user, override this method to reset focus when the
    * containing widget is refreshed.
    * </p>
    */
-  public boolean resetFocus(Element parent, C value, Object key) {
+  public boolean resetFocus(Context context, Element parent, C value) {
     return false;
   }
 
-  public void setValue(Element parent, C value, Object key) {
+  public void setValue(Context context, Element parent, C value) {
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    render(value, key, sb);
+    render(context, value, sb);
     parent.setInnerHTML(sb.toSafeHtml().asString());
   }
 
@@ -138,20 +139,20 @@
    * should override this method to provide a consistent user experience. Your
    * widget must consume <code>keydown</code> events for this method to be
    * called.
-   *
+   * 
+   * @param context the {@link Context} of the cell
    * @param parent the parent Element
    * @param value the value associated with the cell
-   * @param key the unique key associated with the row object
    * @param event the native browser event
    * @param valueUpdater a {@link ValueUpdater}, or null if not specified
    */
-  protected void onEnterKeyDown(Element parent, C value, Object key,
+  protected void onEnterKeyDown(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
   }
 
   /**
    * Initialize the cell.
-   *
+   * 
    * @param consumedEvents the events that the cell consumes
    */
   private void init(Set<String> consumedEvents) {
diff --git a/user/src/com/google/gwt/cell/client/AbstractEditableCell.java b/user/src/com/google/gwt/cell/client/AbstractEditableCell.java
index a99eb8f..66a4bcb 100644
--- a/user/src/com/google/gwt/cell/client/AbstractEditableCell.java
+++ b/user/src/com/google/gwt/cell/client/AbstractEditableCell.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Element;
 
 import java.util.HashMap;
@@ -76,7 +77,7 @@
    *
    * @param key the key identifying the row object
    * @return the view data, or null if none has been set
-   * @see #setViewData(Object, Object)V
+   * @see #setViewData(Object, Object)
    */
   public V getViewData(Object key) {
     return (key == null) ? null : viewDataMap.get(key);
@@ -87,14 +88,14 @@
    * given element and key. While a cell is editing, widgets containing the cell
    * may choose to pass keystrokes directly to the cell rather than using them
    * for navigation purposes.
-   *
+   * 
+   * @param context the {@link Context} of the cell
    * @param parent the parent Element
    * @param value the value associated with the cell
-   * @param key the unique key associated with the row object
    * @return true if the cell is in edit mode
    */
   @Override
-  public abstract boolean isEditing(Element parent, C value, Object key);
+  public abstract boolean isEditing(Context context, Element parent, C value);
 
   /**
    * Associate view data with the specified key. If the key is null, the view
diff --git a/user/src/com/google/gwt/cell/client/AbstractInputCell.java b/user/src/com/google/gwt/cell/client/AbstractInputCell.java
index e67ccf5..b9719fc 100644
--- a/user/src/com/google/gwt/cell/client/AbstractInputCell.java
+++ b/user/src/com/google/gwt/cell/client/AbstractInputCell.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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
@@ -23,7 +23,7 @@
 
 /**
  * An {@link AbstractCell} used to render input elements that can receive focus.
- *
+ * 
  * @param <C> the type that this Cell represents
  * @param <V> the data type of the view data state
  */
@@ -32,7 +32,7 @@
 
   /**
    * Get the events consumed by the input cell.
-   *
+   * 
    * @param userEvents the events consumed by the subclass
    * @return the events
    */
@@ -49,7 +49,7 @@
 
   /**
    * Get the events consumed by the input cell.
-   *
+   * 
    * @param userEvents the events consumed by the subclass
    * @return the events
    */
@@ -71,7 +71,7 @@
   /**
    * Construct a new {@link AbstractInputCell} with the specified consumed
    * events.
-   *
+   * 
    * @param consumedEvents the events that this cell consumes
    */
   public AbstractInputCell(String... consumedEvents) {
@@ -81,7 +81,7 @@
   /**
    * Construct a new {@link AbstractInputCell} with the specified consumed
    * events.
-   *
+   * 
    * @param consumedEvents the events that this cell consumes
    */
   public AbstractInputCell(Set<String> consumedEvents) {
@@ -89,14 +89,14 @@
   }
 
   @Override
-  public boolean isEditing(Element parent, C value, Object key) {
-    return focusedKey != null && focusedKey.equals(key);
+  public boolean isEditing(Context context, Element parent, C value) {
+    return focusedKey != null && focusedKey.equals(context.getKey());
   }
 
   @Override
-  public void onBrowserEvent(Element parent, C value, Object key,
+  public void onBrowserEvent(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
 
     // Ignore events that don't target the input.
     Element target = event.getEventTarget().cast();
@@ -106,15 +106,15 @@
 
     String eventType = event.getType();
     if ("focus".equals(eventType)) {
-      focusedKey = key;
+      focusedKey = context.getKey();
     } else if ("blur".equals(eventType)) {
       focusedKey = null;
     }
   }
 
   @Override
-  public boolean resetFocus(Element parent, C value, Object key) {
-    if (isEditing(parent, value, key)) {
+  public boolean resetFocus(Context context, Element parent, C value) {
+    if (isEditing(context, parent, value)) {
       getInputElement(parent).focus();
       return true;
     }
@@ -123,7 +123,7 @@
 
   /**
    * Call this method when editing is complete.
-   *
+   * 
    * @param parent the parent Element
    * @param value the value associated with the cell
    * @param key the unique key associated with the row object
@@ -137,7 +137,7 @@
 
   /**
    * Get the input element.
-   *
+   * 
    * @param parent the cell parent element
    * @return the input element
    */
@@ -146,10 +146,11 @@
   }
 
   @Override
-  protected void onEnterKeyDown(Element parent, C value, Object key,
+  protected void onEnterKeyDown(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
     Element input = getInputElement(parent);
     Element target = event.getEventTarget().cast();
+    Object key = context.getKey();
     if (getInputElement(parent).isOrHasChild(target)) {
       finishEditing(parent, value, key, valueUpdater);
     } else {
diff --git a/user/src/com/google/gwt/cell/client/AbstractSafeHtmlCell.java b/user/src/com/google/gwt/cell/client/AbstractSafeHtmlCell.java
index 4d7bc32..73f7ac7 100644
--- a/user/src/com/google/gwt/cell/client/AbstractSafeHtmlCell.java
+++ b/user/src/com/google/gwt/cell/client/AbstractSafeHtmlCell.java
@@ -73,11 +73,11 @@
   }
 
   @Override
-  public void render(C data, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, C data, SafeHtmlBuilder sb) {
     if (data == null) {
-      render((SafeHtml) null, key, sb);
+      render(context, (SafeHtml) null, sb);
     } else {
-      render(renderer.render(data), key, sb);
+      render(context, renderer.render(data), sb);
     }
   }
 
@@ -85,9 +85,10 @@
    * Render the cell contents after they have been converted to {@link SafeHtml}
    * form.
    * 
+   * @param context the original context to render
    * @param data a {@link SafeHtml} string
-   * @param key the unique key associated with the row object
    * @param sb the {@link SafeHtmlBuilder} to be written to
    */
-  protected abstract void render(SafeHtml data, Object key, SafeHtmlBuilder sb);
+  protected abstract void render(Context context, SafeHtml data,
+      SafeHtmlBuilder sb);
 }
diff --git a/user/src/com/google/gwt/cell/client/ActionCell.java b/user/src/com/google/gwt/cell/client/ActionCell.java
index 0003118..c72c63d 100644
--- a/user/src/com/google/gwt/cell/client/ActionCell.java
+++ b/user/src/com/google/gwt/cell/client/ActionCell.java
@@ -72,21 +72,21 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, C value, Object key,
+  public void onBrowserEvent(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
     if ("click".equals(event.getType())) {
-      onEnterKeyDown(parent, value, key, event, valueUpdater);
+      onEnterKeyDown(context, parent, value, event, valueUpdater);
     }
   }
 
   @Override
-  public void render(C value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, C value, SafeHtmlBuilder sb) {
     sb.append(html);
   }
 
   @Override
-  protected void onEnterKeyDown(Element parent, C value, Object key,
+  protected void onEnterKeyDown(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
     delegate.execute(value);
   }
diff --git a/user/src/com/google/gwt/cell/client/ButtonCell.java b/user/src/com/google/gwt/cell/client/ButtonCell.java
index d8caa83..cabd39e 100644
--- a/user/src/com/google/gwt/cell/client/ButtonCell.java
+++ b/user/src/com/google/gwt/cell/client/ButtonCell.java
@@ -44,16 +44,16 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, String value, Object key,
+  public void onBrowserEvent(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
     if ("click".equals(event.getType())) {
-      onEnterKeyDown(parent, value, key, event, valueUpdater);
+      onEnterKeyDown(context, parent, value, event, valueUpdater);
     }
   }
 
   @Override
-  public void render(SafeHtml data, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, SafeHtml data, SafeHtmlBuilder sb) {
     sb.appendHtmlConstant("<button type=\"button\" tabindex=\"-1\">");
     if (data != null) {
       sb.append(data);
@@ -62,7 +62,7 @@
   }
 
   @Override
-  protected void onEnterKeyDown(Element parent, String value, Object key,
+  protected void onEnterKeyDown(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
     if (valueUpdater != null) {
       valueUpdater.update(value);
diff --git a/user/src/com/google/gwt/cell/client/Cell.java b/user/src/com/google/gwt/cell/client/Cell.java
index 217217f..2f32358 100644
--- a/user/src/com/google/gwt/cell/client/Cell.java
+++ b/user/src/com/google/gwt/cell/client/Cell.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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
@@ -23,19 +23,70 @@
 
 /**
  * A light weight representation of a renderable object.
- *
+ * 
  * <p>
  * <h3>Example</h3>
  * {@example com.google.gwt.examples.cell.CellExample}
  * </p>
- *
+ * 
  * @param <C> the type that this Cell represents
  */
 public interface Cell<C> {
 
   /**
+   * Contains information about the context of the Cell.
+   */
+  public static class Context {
+
+    private final int column;
+    private final int index;
+    private final Object key;
+
+    /**
+     * Create a new {@link Context}.
+     * 
+     * @param index the absolute index of the value
+     * @param column the column index of the cell, or 0
+     * @param key the unique key that represents the row value
+     */
+    public Context(int index, int column, Object key) {
+      this.index = index;
+      this.column = column;
+      this.key = key;
+    }
+
+    /**
+     * Get the column index of the cell. If the view only contains a single
+     * column, this method returns 0.
+     * 
+     * @return the column index of the cell
+     */
+    public int getColumn() {
+      return column;
+    }
+
+    /**
+     * Get the absolute index of the value.
+     * 
+     * @return the index
+     */
+    public int getIndex() {
+      return index;
+    }
+
+    /**
+     * Get the key that uniquely identifies the row object.
+     * 
+     * @return the unique key
+     */
+    public Object getKey() {
+      return key;
+    }
+  }
+
+  /**
    * Check if this cell depends on the selection state.
-   *
+   * 
    * @return true if dependent on selection, false if not
    */
   boolean dependsOnSelection();
@@ -43,13 +94,14 @@
   /**
    * Get the set of events that this cell consumes. The container that uses this
    * cell should only pass these events to
-   * {@link #onBrowserEvent(Element, Object, Object, NativeEvent, ValueUpdater)}.
-   *
+   * {@link #onBrowserEvent(Context, Element, Object, NativeEvent, ValueUpdater))}
+   * when the event occurs.
+   * 
    * <p>
    * The returned value should not be modified, and may be an unmodifiable set.
    * Changes to the return value may not be reflected in the cell.
    * </p>
-   *
+   * 
    * @return the consumed events, or null if no events are consumed
    */
   Set<String> getConsumedEvents();
@@ -57,7 +109,7 @@
   /**
    * Check if this cell handles selection. If the cell handles selection, then
    * its container should not automatically handle selection.
-   *
+   * 
    * @return true if the cell handles selection, false if not
    */
   boolean handlesSelection();
@@ -65,32 +117,32 @@
   /**
    * Returns true if the cell is currently editing the data identified by the
    * given element and key. While a cell is editing, widgets containing the cell
-   * may chooses to pass keystrokes directly to the cell rather than using them
+   * may choose to pass keystrokes directly to the cell rather than using them
    * for navigation purposes.
-   *
+   * 
+   * @param context the {@link Context} of the cell
    * @param parent the parent Element
    * @param value the value associated with the cell
-   * @param key the unique key associated with the row object
    * @return true if the cell is in edit mode
    */
-  boolean isEditing(Element parent, C value, Object key);
+  boolean isEditing(Context context, Element parent, C value);
 
   /**
    * Handle a browser event that took place within the cell. The default
    * implementation returns null.
-   *
+   * 
+   * @param context the {@link Context} of the cell
    * @param parent the parent Element
    * @param value the value associated with the cell
-   * @param key the unique key associated with the row object
    * @param event the native browser event
    * @param valueUpdater a {@link ValueUpdater}, or null if not specified
    */
-  void onBrowserEvent(Element parent, C value, Object key, NativeEvent event,
-      ValueUpdater<C> valueUpdater);
+  void onBrowserEvent(Context context, Element parent, C value,
+      NativeEvent event, ValueUpdater<C> valueUpdater);
 
   /**
    * Render a cell as HTML into a {@link SafeHtmlBuilder}, suitable for passing
-   * to {@link Element#setInnerHTML} on a container element.
+   * to {@link Element#setInnerHTML(String)} on a container element.
    * 
    * <p>
    * Note: If your cell contains natively focusable elements, such as buttons or
@@ -98,33 +150,32 @@
    * focus away from the containing widget.
    * </p>
    * 
+   * @param context the {@link Context} of the cell
    * @param value the cell value to be rendered
-   * @param key the unique key associated with the row object
    * @param sb the {@link SafeHtmlBuilder} to be written to
    */
-  void render(C value, Object key, SafeHtmlBuilder sb);
+  void render(Context context, C value, SafeHtmlBuilder sb);
 
   /**
    * Reset focus on the Cell. This method is called if the cell has focus when
    * it is refreshed.
-   *
+   * 
+   * @param context the {@link Context} of the cell
    * @param parent the parent Element
    * @param value the value associated with the cell
-   * @param key the unique key associated with the row object
-   *
    * @return true if focus is taken, false if not
    */
-  boolean resetFocus(Element parent, C value, Object key);
+  boolean resetFocus(Context context, Element parent, C value);
 
   /**
    * This method may be used by cell containers to set the value on a single
    * cell directly, rather than using {@link Element#setInnerHTML(String)}. See
-   * {@link AbstractCell#setValue(Element, Object, Object)} for a default
-   * implementation that uses {@link #render(Object, Object, SafeHtmlBuilder)}.
-   *
+   * {@link AbstractCell#setValue(Context)} for a default implementation that
+   * uses {@link #render(Context, SafeHtmlBuilder))}.
+   * 
+   * @param context the {@link Context} of the cell
    * @param parent the parent Element
    * @param value the value associated with the cell
-   * @param key the unique key associated with the row object
    */
-  void setValue(Element parent, C value, Object key);
+  void setValue(Context context, Element parent, C value);
 }
diff --git a/user/src/com/google/gwt/cell/client/CheckboxCell.java b/user/src/com/google/gwt/cell/client/CheckboxCell.java
index 1f51a1d..e0eae00 100644
--- a/user/src/com/google/gwt/cell/client/CheckboxCell.java
+++ b/user/src/com/google/gwt/cell/client/CheckboxCell.java
@@ -83,14 +83,14 @@
   }
 
   @Override
-  public boolean isEditing(Element parent, Boolean value, Object key) {
+  public boolean isEditing(Context context, Element parent, Boolean value) {
     // A checkbox is never in "edit mode". There is no intermediate state
     // between checked and unchecked.
     return false;
   }
 
   @Override
-  public void onBrowserEvent(Element parent, Boolean value, Object key,
+  public void onBrowserEvent(Context context, Element parent, Boolean value, 
       NativeEvent event, ValueUpdater<Boolean> valueUpdater) {
     String type = event.getType();
 
@@ -117,9 +117,9 @@
        * do not save the value because we can get into an inconsistent state.
        */
       if (value != isChecked && !dependsOnSelection()) {
-        setViewData(key, isChecked);
+        setViewData(context.getKey(), isChecked);
       } else {
-        clearViewData(key);
+        clearViewData(context.getKey());
       }
 
       if (valueUpdater != null) {
@@ -129,8 +129,9 @@
   }
 
   @Override
-  public void render(Boolean value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, Boolean value, SafeHtmlBuilder sb) {
     // Get the view data.
+    Object key = context.getKey();
     Boolean viewData = getViewData(key);
     if (viewData != null && viewData.equals(value)) {
       clearViewData(key);
diff --git a/user/src/com/google/gwt/cell/client/ClickableTextCell.java b/user/src/com/google/gwt/cell/client/ClickableTextCell.java
index 16fbe38..a3a41d7 100644
--- a/user/src/com/google/gwt/cell/client/ClickableTextCell.java
+++ b/user/src/com/google/gwt/cell/client/ClickableTextCell.java
@@ -47,16 +47,16 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, String value, Object key,
+  public void onBrowserEvent(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
     if ("click".equals(event.getType())) {
-      onEnterKeyDown(parent, value, key, event, valueUpdater);
+      onEnterKeyDown(context, parent, value, event, valueUpdater);
     }
   }
 
   @Override
-  protected void onEnterKeyDown(Element parent, String value, Object key,
+  protected void onEnterKeyDown(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
     if (valueUpdater != null) {
       valueUpdater.update(value);
@@ -64,7 +64,7 @@
   }
 
   @Override
-  protected void render(SafeHtml value, Object key, SafeHtmlBuilder sb) {
+  protected void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
     if (value != null) {
       sb.append(value);
     }
diff --git a/user/src/com/google/gwt/cell/client/CompositeCell.java b/user/src/com/google/gwt/cell/client/CompositeCell.java
index 6013d46..c1328b6 100644
--- a/user/src/com/google/gwt/cell/client/CompositeCell.java
+++ b/user/src/com/google/gwt/cell/client/CompositeCell.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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
@@ -30,13 +30,13 @@
  * <p>
  * A {@link Cell} that is composed of other {@link Cell}s.
  * </p>
- *
+ * 
  * <p>
  * When this cell is rendered, it will render each component {@link Cell} inside
  * a span. If the component {@link Cell} uses block level elements (such as a
  * Div), the component cells will stack vertically.
  * </p>
- *
+ * 
  * @param <C> the type that this Cell represents
  */
 public class CompositeCell<C> extends AbstractCell<C> {
@@ -58,7 +58,7 @@
 
   /**
    * The cells that compose this {@link Cell}.
-   *
+   * 
    * NOTE: Do not add add/insert/remove hasCells methods to the API. This cell
    * assumes that the index of the cellParent corresponds to the index in the
    * hasCells array.
@@ -67,7 +67,7 @@
 
   /**
    * Construct a new {@link CompositeCell}.
-   *
+   * 
    * @param hasCells the cells that makeup the composite
    */
   public CompositeCell(List<HasCell<C, ?>> hasCells) {
@@ -113,7 +113,7 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, C value, Object key,
+  public void onBrowserEvent(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
     int index = 0;
     EventTarget eventTarget = event.getEventTarget();
@@ -123,7 +123,7 @@
       Element wrapper = container.getFirstChildElement();
       while (wrapper != null) {
         if (wrapper.isOrHasChild(target)) {
-          onBrowserEventImpl(wrapper, value, key, event, valueUpdater,
+          onBrowserEventImpl(context, wrapper, value, event, valueUpdater,
               hasCells.get(index));
         }
 
@@ -134,19 +134,19 @@
   }
 
   @Override
-  public void render(C value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, C value, SafeHtmlBuilder sb) {
     for (HasCell<C, ?> hasCell : hasCells) {
-      render(value, key, sb, hasCell);
+      render(context, value, sb, hasCell);
     }
   }
 
   @Override
-  public boolean resetFocus(Element parent, C value, Object key) {
+  public boolean resetFocus(Context context, Element parent, C value) {
     Element curChild = getContainerElement(parent).getFirstChildElement();
     for (HasCell<C, ?> hasCell : hasCells) {
       // The first child that takes focus wins. Only one child should ever be in
       // edit mode, so this is safe.
-      if (resetFocusImpl(curChild, value, key, hasCell)) {
+      if (resetFocusImpl(context, curChild, value, hasCell)) {
         return true;
       }
       curChild = curChild.getNextSiblingElement();
@@ -155,10 +155,10 @@
   }
 
   @Override
-  public void setValue(Element parent, C object, Object key) {
+  public void setValue(Context context, Element parent, C object) {
     Element curChild = getContainerElement(parent).getFirstChildElement();
     for (HasCell<C, ?> hasCell : hasCells) {
-      setValueImpl(curChild, object, key, hasCell);
+      setValueImpl(context, curChild, object, hasCell);
       curChild = curChild.getNextSiblingElement();
     }
   }
@@ -167,7 +167,7 @@
    * Get the element that acts as the container for all children. If children
    * are added directly to the parent, the parent is the container. If children
    * are added in a table row, the row is the parent.
-   *
+   * 
    * @param parent the parent element of the cell
    * @return the container element
    */
@@ -185,22 +185,22 @@
    * focus away from the containing widget.
    * </p>
    * 
+   * @param context the {@link Context} of the cell
    * @param value the cell value to be rendered
-   * @param key the unique key associated with the row object
    * @param sb the {@link SafeHtmlBuilder} to be written to
    * @param hasCell a {@link HasCell} instance containing the cells to be
    *          rendered within this cell
    */
-  protected <X> void render(C value, Object key, SafeHtmlBuilder sb,
-      HasCell<C, X> hasCell) {
+  protected <X> void render(Context context, C value,
+      SafeHtmlBuilder sb, HasCell<C, X> hasCell) {
     Cell<X> cell = hasCell.getCell();
     sb.appendHtmlConstant("<span>");
-    cell.render(hasCell.getValue(value), key, sb);
+    cell.render(context, hasCell.getValue(value), sb);
     sb.appendHtmlConstant("</span>");
   }
 
-  private <X> void onBrowserEventImpl(Element parent, final C object,
-      Object key, NativeEvent event, final ValueUpdater<C> valueUpdater,
+  private <X> void onBrowserEventImpl(Context context, Element parent,
+      final C object, NativeEvent event, final ValueUpdater<C> valueUpdater,
       final HasCell<C, X> hasCell) {
     ValueUpdater<X> tempUpdater = null;
     final FieldUpdater<C, X> fieldUpdater = hasCell.getFieldUpdater();
@@ -215,18 +215,18 @@
       };
     }
     Cell<X> cell = hasCell.getCell();
-    cell.onBrowserEvent(parent, hasCell.getValue(object), key, event,
+    cell.onBrowserEvent(context, parent, hasCell.getValue(object), event,
         tempUpdater);
   }
 
-  private <X> boolean resetFocusImpl(Element cellParent, C value, Object key,
-      HasCell<C, X> hasCell) {
+  private <X> boolean resetFocusImpl(Context context, Element cellParent,
+      C value, HasCell<C, X> hasCell) {
     X cellValue = hasCell.getValue(value);
-    return hasCell.getCell().resetFocus(cellParent, cellValue, key);
+    return hasCell.getCell().resetFocus(context, cellParent, cellValue);
   }
 
-  private <X> void setValueImpl(Element cellParent, C object, Object key,
+  private <X> void setValueImpl(Context context, Element cellParent, C object,
       HasCell<C, X> hasCell) {
-    hasCell.getCell().setValue(cellParent, hasCell.getValue(object), key);
-  }
+    hasCell.getCell().setValue(context, cellParent, hasCell.getValue(object));
+  }  
 }
diff --git a/user/src/com/google/gwt/cell/client/DateCell.java b/user/src/com/google/gwt/cell/client/DateCell.java
index bfab2af..bed5b8b 100644
--- a/user/src/com/google/gwt/cell/client/DateCell.java
+++ b/user/src/com/google/gwt/cell/client/DateCell.java
@@ -82,7 +82,7 @@
   }
 
   @Override
-  public void render(Date value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, Date value, SafeHtmlBuilder sb) {
     if (value != null) {
       sb.append(renderer.render(format.format(value)));
     }
diff --git a/user/src/com/google/gwt/cell/client/DatePickerCell.java b/user/src/com/google/gwt/cell/client/DatePickerCell.java
index 49df900..4b1252b 100644
--- a/user/src/com/google/gwt/cell/client/DatePickerCell.java
+++ b/user/src/com/google/gwt/cell/client/DatePickerCell.java
@@ -61,6 +61,8 @@
   private int offsetY = 10;
   private Object lastKey;
   private Element lastParent;
+  private int lastIndex;
+  private int lastColumn;
   private Date lastValue;
   private PopupPanel panel;
   private final SafeHtmlRenderer<String> renderer;
@@ -131,6 +133,8 @@
       public void onClose(CloseEvent<PopupPanel> event) {
         lastKey = null;
         lastValue = null;
+        lastIndex = -1;
+        lastColumn = -1;
         if (lastParent != null && !event.isAutoClosed()) {
           // Refocus on the containing cell after the user selects a value, but
           // not if the popup is auto closed.
@@ -148,12 +152,14 @@
         Element cellParent = lastParent;
         Date oldValue = lastValue;
         Object key = lastKey;
+        int index = lastIndex;
+        int column = lastColumn;
         panel.hide();
 
         // Update the cell and value updater.
         Date date = event.getValue();
         setViewData(key, date);
-        setValue(cellParent, oldValue, key);
+        setValue(new Context(index, column, key), cellParent, oldValue);
         if (valueUpdater != null) {
           valueUpdater.update(date);
         }
@@ -162,22 +168,23 @@
   }
 
   @Override
-  public boolean isEditing(Element parent, Date value, Object key) {
-    return lastKey != null && lastKey.equals(key);
+  public boolean isEditing(Context context, Element parent, Date value) {
+    return lastKey != null && lastKey.equals(context.getKey());
   }
 
   @Override
-  public void onBrowserEvent(Element parent, Date value, Object key,
+  public void onBrowserEvent(Context context, Element parent, Date value,
       NativeEvent event, ValueUpdater<Date> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
     if ("click".equals(event.getType())) {
-      onEnterKeyDown(parent, value, key, event, valueUpdater);
+      onEnterKeyDown(context, parent, value, event, valueUpdater);
     }
   }
 
   @Override
-  public void render(Date value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, Date value, SafeHtmlBuilder sb) {
     // Get the view data.
+    Object key = context.getKey();
     Date viewData = getViewData(key);
     if (viewData != null && viewData.equals(value)) {
       clearViewData(key);
@@ -196,21 +203,23 @@
   }
 
   @Override
-  protected void onEnterKeyDown(final Element parent, Date value, Object key,
+  protected void onEnterKeyDown(Context context, Element parent, Date value,
       NativeEvent event, ValueUpdater<Date> valueUpdater) {
-    this.lastKey = key;
+    this.lastKey = context.getKey();
     this.lastParent = parent;
     this.lastValue = value;
+    this.lastIndex = context.getIndex();
+    this.lastColumn = context.getColumn();
     this.valueUpdater = valueUpdater;
 
-    Date viewData = getViewData(key);
-    Date date = (viewData == null) ? value : viewData;
+    Date viewData = getViewData(lastKey);
+    Date date = (viewData == null) ? lastValue : viewData;
     datePicker.setCurrentMonth(date);
     datePicker.setValue(date);
     panel.setPopupPositionAndShow(new PositionCallback() {
       public void setPosition(int offsetWidth, int offsetHeight) {
-        panel.setPopupPosition(parent.getAbsoluteLeft() + offsetX,
-            parent.getAbsoluteTop() + offsetY);
+        panel.setPopupPosition(lastParent.getAbsoluteLeft() + offsetX,
+            lastParent.getAbsoluteTop() + offsetY);
       }
     });
   }
diff --git a/user/src/com/google/gwt/cell/client/EditTextCell.java b/user/src/com/google/gwt/cell/client/EditTextCell.java
index 57f214f..e180a69 100644
--- a/user/src/com/google/gwt/cell/client/EditTextCell.java
+++ b/user/src/com/google/gwt/cell/client/EditTextCell.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.EventTarget;
@@ -161,18 +162,19 @@
   }
 
   @Override
-  public boolean isEditing(Element element, String value, Object key) {
-    ViewData viewData = getViewData(key);
+  public boolean isEditing(Context context, Element parent, String value) {
+    ViewData viewData = getViewData(context.getKey());
     return viewData == null ? false : viewData.isEditing();
   }
 
   @Override
-  public void onBrowserEvent(Element parent, String value, Object key,
+  public void onBrowserEvent(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
+    Object key = context.getKey();
     ViewData viewData = getViewData(key);
     if (viewData != null && viewData.isEditing()) {
       // Handle the edit event.
-      editEvent(parent, key, viewData, event, valueUpdater);
+      editEvent(context, parent, value, viewData, event, valueUpdater);
     } else {
       String type = event.getType();
       int keyCode = event.getKeyCode();
@@ -186,14 +188,15 @@
         } else {
           viewData.setEditing(true);
         }
-        edit(parent, value, key);
+        edit(context, parent, value);
       }
     }
   }
 
   @Override
-  public void render(String value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, String value, SafeHtmlBuilder sb) {
     // Get the view data.
+    Object key = context.getKey();
     ViewData viewData = getViewData(key);
     if (viewData != null && !viewData.isEditing() && value != null
         && value.equals(viewData.getText())) {
@@ -218,8 +221,8 @@
   }
 
   @Override
-  public boolean resetFocus(Element parent, String value, Object key) {
-    if (isEditing(parent, value, key)) {
+  public boolean resetFocus(Context context, Element parent, String value) {
+    if (isEditing(context, parent, value)) {
       getInputElement(parent).focus();
       return true;
     }
@@ -229,12 +232,12 @@
   /**
    * Convert the cell to edit mode.
    *
+   * @param context the {@link Context} of the cell
    * @param parent the parent element
    * @param value the current value
-   * @param key the key of the row object
    */
-  protected void edit(Element parent, String value, Object key) {
-    setValue(parent, value, key);
+  protected void edit(Context context, Element parent, String value) {
+    setValue(context, parent, value);
     InputElement input = getInputElement(parent);
     input.focus();
     input.select();
@@ -242,13 +245,14 @@
 
   /**
    * Convert the cell to non-edit mode.
-   *
-   * @param parent the parent element
-   * @param value the current value
+   * 
+   * @param context the context of the cell
+   * @param parent the parent Element
+   * @param value the value associated with the cell
    */
-  private void cancel(Element parent, String value) {
+  private void cancel(Context context, Element parent, String value) {
     clearInput(getInputElement(parent));
-    setValue(parent, value, null);
+    setValue(context, parent, value);
   }
 
   /**
@@ -267,23 +271,24 @@
 
   /**
    * Commit the current value.
-   *
-   * @param parent the parent element
+   * 
+   * @param context the context of the cell
+   * @param parent the parent Element
    * @param viewData the {@link ViewData} object
    * @param valueUpdater the {@link ValueUpdater}
    */
-  private void commit(Element parent, ViewData viewData,
+  private void commit(Context context, Element parent, ViewData viewData,
       ValueUpdater<String> valueUpdater) {
     String value = updateViewData(parent, viewData, false);
     clearInput(getInputElement(parent));
-    setValue(parent, value, viewData);
+    setValue(context, parent, viewData.getOriginal());
     if (valueUpdater != null) {
       valueUpdater.update(value);
     }
   }
 
-  private void editEvent(Element parent, Object key, ViewData viewData,
-      NativeEvent event, ValueUpdater<String> valueUpdater) {
+  private void editEvent(Context context, Element parent, String value,
+      ViewData viewData, NativeEvent event, ValueUpdater<String> valueUpdater) {
     String type = event.getType();
     boolean keyUp = "keyup".equals(type);
     boolean keyDown = "keydown".equals(type);
@@ -291,7 +296,7 @@
       int keyCode = event.getKeyCode();
       if (keyUp && keyCode == KeyCodes.KEY_ENTER) {
         // Commit the change.
-        commit(parent, viewData, valueUpdater);
+        commit(context, parent, viewData, valueUpdater);
       } else if (keyUp && keyCode == KeyCodes.KEY_ESCAPE) {
         // Cancel edit mode.
         String originalText = viewData.getOriginal();
@@ -299,9 +304,9 @@
           viewData.setText(originalText);
           viewData.setEditing(false);
         } else {
-          setViewData(key, null);
+          setViewData(context.getKey(), null);
         }
-        cancel(parent, originalText);
+        cancel(context, parent, value);
       } else {
         // Update the text in the view data on each key.
         updateViewData(parent, viewData, true);
@@ -313,7 +318,7 @@
       if (Element.is(eventTarget)) {
         Element target = Element.as(eventTarget);
         if ("input".equals(target.getTagName().toLowerCase())) {
-          commit(parent, viewData, valueUpdater);
+          commit(context, parent, viewData, valueUpdater);
         }
       }
     }
diff --git a/user/src/com/google/gwt/cell/client/IconCellDecorator.java b/user/src/com/google/gwt/cell/client/IconCellDecorator.java
index 496ac3c..3e0c7da 100644
--- a/user/src/com/google/gwt/cell/client/IconCellDecorator.java
+++ b/user/src/com/google/gwt/cell/client/IconCellDecorator.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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
@@ -32,7 +32,7 @@
 
 /**
  * A {@link Cell} decorator that adds an icon to another {@link Cell}.
- *
+ * 
  * @param <C> the type that this Cell represents
  */
 public class IconCellDecorator<C> implements Cell<C> {
@@ -83,7 +83,7 @@
   /**
    * Construct a new {@link IconCellDecorator}. The icon and the content will be
    * middle aligned by default.
-   *
+   * 
    * @param icon the icon to use
    * @param cell the cell to decorate
    */
@@ -93,7 +93,7 @@
 
   /**
    * Construct a new {@link IconCellDecorator}.
-   *
+   * 
    * @param icon the icon to use
    * @param cell the cell to decorate
    * @param valign the vertical alignment attribute of the contents
@@ -122,35 +122,36 @@
     return cell.handlesSelection();
   }
 
-  public boolean isEditing(Element element, C value, Object key) {
-    return cell.isEditing(element, value, key);
+  public boolean isEditing(Context context, Element parent, C value) {
+    return cell.isEditing(context, getCellParent(parent), value);
   }
 
-  public void onBrowserEvent(Element parent, C value, Object key,
+  public void onBrowserEvent(Context context, Element parent, C value,
       NativeEvent event, ValueUpdater<C> valueUpdater) {
-    cell.onBrowserEvent(getCellParent(parent), value, key, event, valueUpdater);
+    cell.onBrowserEvent(context, getCellParent(parent), value, event,
+        valueUpdater);
   }
 
-  public void render(C value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, C value, SafeHtmlBuilder sb) {
     SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
-    cell.render(value, key, cellBuilder);
+    cell.render(context, value, cellBuilder);
 
     sb.append(template.outerDiv(direction, imageWidth, isIconUsed(value)
         ? getIconHtml(value) : placeHolderHtml, cellBuilder.toSafeHtml()));
   }
 
-  public boolean resetFocus(Element parent, C value, Object key) {
-    return cell.resetFocus(getCellParent(parent), value, key);
+  public boolean resetFocus(Context context, Element parent, C value) {
+    return cell.resetFocus(context, getCellParent(parent), value);
   }
 
-  public void setValue(Element parent, C value, Object key) {
-    cell.setValue(getCellParent(parent), value, key);
+  public void setValue(Context context, Element parent, C value) {
+    cell.setValue(context, getCellParent(parent), value);
   }
 
   /**
    * Get the safe HTML string that represents the icon. Override this method to
    * change the icon based on the value.
-   *
+   * 
    * @param value the value being rendered
    * @return the HTML string that represents the icon
    */
@@ -162,7 +163,7 @@
    * Check if the icon should be used for the value. If the icon should not be
    * used, a placeholder of the same size will be used instead. The default
    * implementations returns true.
-   *
+   * 
    * @param value the value being rendered
    * @return true to use the icon, false to use a placeholder
    */
@@ -172,7 +173,7 @@
 
   /**
    * Get the HTML representation of an image. Visible for testing.
-   *
+   * 
    * @param res the {@link ImageResource} to render as HTML
    * @param valign the vertical alignment
    * @param isPlaceholder if true, do not include the background image
@@ -203,7 +204,7 @@
 
   /**
    * Get the parent element of the decorated cell.
-   *
+   * 
    * @param parent the parent of this cell
    * @return the decorated cell's parent
    */
diff --git a/user/src/com/google/gwt/cell/client/ImageCell.java b/user/src/com/google/gwt/cell/client/ImageCell.java
index 25f173d..18ea3c8 100644
--- a/user/src/com/google/gwt/cell/client/ImageCell.java
+++ b/user/src/com/google/gwt/cell/client/ImageCell.java
@@ -51,7 +51,7 @@
   }
 
   @Override
-  public void render(String value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, String value, SafeHtmlBuilder sb) {
     if (value != null) {
       // The template will sanitize the URI.
       sb.append(template.img(value));
diff --git a/user/src/com/google/gwt/cell/client/ImageLoadingCell.java b/user/src/com/google/gwt/cell/client/ImageLoadingCell.java
index 67a3e23..e72e7a4 100644
--- a/user/src/com/google/gwt/cell/client/ImageLoadingCell.java
+++ b/user/src/com/google/gwt/cell/client/ImageLoadingCell.java
@@ -179,7 +179,7 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, String value, Object key,
+  public void onBrowserEvent(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
     // The loading indicator can fire its own load or error event, so we check
     // that the event actually occurred on the main image.
@@ -201,7 +201,7 @@
   }
 
   @Override
-  public void render(String value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, String value, SafeHtmlBuilder sb) {
     // We can't use ViewData because we don't know the caching policy of the
     // browser. The browser may fetch the image every time we render.
     if (value != null) {
diff --git a/user/src/com/google/gwt/cell/client/ImageResourceCell.java b/user/src/com/google/gwt/cell/client/ImageResourceCell.java
index ce9fb37..c2a0c8c 100644
--- a/user/src/com/google/gwt/cell/client/ImageResourceCell.java
+++ b/user/src/com/google/gwt/cell/client/ImageResourceCell.java
@@ -40,7 +40,7 @@
   }
 
   @Override
-  public void render(ImageResource value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, ImageResource value, SafeHtmlBuilder sb) {
     if (value != null) {
       SafeHtml html = SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(
           value).getHTML());
diff --git a/user/src/com/google/gwt/cell/client/NumberCell.java b/user/src/com/google/gwt/cell/client/NumberCell.java
index 14b301b..cdf0201 100644
--- a/user/src/com/google/gwt/cell/client/NumberCell.java
+++ b/user/src/com/google/gwt/cell/client/NumberCell.java
@@ -84,7 +84,7 @@
   }
 
   @Override
-  public void render(Number value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, Number value, SafeHtmlBuilder sb) {
     if (value != null) {
       sb.append(renderer.render(format.format(value)));
     }
diff --git a/user/src/com/google/gwt/cell/client/SafeHtmlCell.java b/user/src/com/google/gwt/cell/client/SafeHtmlCell.java
index 69f46ed..bb4109b 100644
--- a/user/src/com/google/gwt/cell/client/SafeHtmlCell.java
+++ b/user/src/com/google/gwt/cell/client/SafeHtmlCell.java
@@ -30,7 +30,7 @@
   }
 
   @Override
-  public void render(SafeHtml value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
     if (value != null) {
       sb.append(value);
     }
diff --git a/user/src/com/google/gwt/cell/client/SelectionCell.java b/user/src/com/google/gwt/cell/client/SelectionCell.java
index 79c9b76..1695d19 100644
--- a/user/src/com/google/gwt/cell/client/SelectionCell.java
+++ b/user/src/com/google/gwt/cell/client/SelectionCell.java
@@ -64,11 +64,12 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, String value, Object key,
+  public void onBrowserEvent(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
     String type = event.getType();
     if ("change".equals(type)) {
+      Object key = context.getKey();
       SelectElement select = parent.getFirstChild().cast();
       String newValue = options.get(select.getSelectedIndex());
       setViewData(key, newValue);
@@ -80,8 +81,9 @@
   }
 
   @Override
-  public void render(String value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, String value, SafeHtmlBuilder sb) {
     // Get the view data.
+    Object key = context.getKey();
     String viewData = getViewData(key);
     if (viewData != null && viewData.equals(value)) {
       clearViewData(key);
diff --git a/user/src/com/google/gwt/cell/client/TextCell.java b/user/src/com/google/gwt/cell/client/TextCell.java
index 48db9eb..2df83ec 100644
--- a/user/src/com/google/gwt/cell/client/TextCell.java
+++ b/user/src/com/google/gwt/cell/client/TextCell.java
@@ -44,7 +44,7 @@
   }
 
   @Override
-  public void render(SafeHtml value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
     if (value != null) {
       sb.append(value);
     }
diff --git a/user/src/com/google/gwt/cell/client/TextInputCell.java b/user/src/com/google/gwt/cell/client/TextInputCell.java
index be1f7d5..e90d295 100644
--- a/user/src/com/google/gwt/cell/client/TextInputCell.java
+++ b/user/src/com/google/gwt/cell/client/TextInputCell.java
@@ -156,9 +156,9 @@
   }
 
   @Override
-  public void onBrowserEvent(Element parent, String value, Object key,
+  public void onBrowserEvent(Context context, Element parent, String value,
       NativeEvent event, ValueUpdater<String> valueUpdater) {
-    super.onBrowserEvent(parent, value, key, event, valueUpdater);
+    super.onBrowserEvent(context, parent, value, event, valueUpdater);
 
     // Ignore events that don't target the input.
     InputElement input = getInputElement(parent);
@@ -168,6 +168,7 @@
     }
 
     String eventType = event.getType();
+    Object key = context.getKey();
     if ("change".equals(eventType)) {
       finishEditing(parent, value, key, valueUpdater);
     } else if ("keyup".equals(eventType)) {
@@ -182,8 +183,9 @@
   }
 
   @Override
-  public void render(String value, Object key, SafeHtmlBuilder sb) {
+  public void render(Context context, String value, SafeHtmlBuilder sb) {
     // Get the view data.
+    Object key = context.getKey();
     ViewData viewData = getViewData(key);
     if (viewData != null && viewData.getCurrentValue().equals(value)) {
       clearViewData(key);
diff --git a/user/src/com/google/gwt/editor/client/adapters/ListEditorWrapper.java b/user/src/com/google/gwt/editor/client/adapters/ListEditorWrapper.java
index 18820db..b028a79 100644
--- a/user/src/com/google/gwt/editor/client/adapters/ListEditorWrapper.java
+++ b/user/src/com/google/gwt/editor/client/adapters/ListEditorWrapper.java
@@ -33,6 +33,7 @@
   private final CompositeEditor.EditorChain<T, E> chain;
   private final List<E> editors;
   private final EditorSource<E> editorSource;
+  private final List<T> workingCopy;
 
   public ListEditorWrapper(List<T> backing,
       CompositeEditor.EditorChain<T, E> chain, EditorSource<E> editorSource) {
@@ -40,11 +41,12 @@
     this.chain = chain;
     this.editorSource = editorSource;
     editors = new ArrayList<E>(backing.size());
+    workingCopy = new ArrayList<T>(backing);
   }
 
   @Override
   public void add(int index, T element) {
-    backing.add(index, element);
+    workingCopy.add(index, element);
     E subEditor = editorSource.create(index);
     editors.add(index, subEditor);
     for (int i = index + 1, j = editors.size(); i < j; i++) {
@@ -55,13 +57,12 @@
 
   @Override
   public T get(int index) {
-    return backing.get(index);
+    return workingCopy.get(index);
   }
 
   @Override
   public T remove(int index) {
-    // Try to mutate the list first, in case it is immutable
-    T toReturn = backing.remove(index);
+    T toReturn = workingCopy.remove(index);
     E subEditor = editors.remove(index);
     editorSource.dispose(subEditor);
     for (int i = index, j = editors.size(); i < j; i++) {
@@ -73,15 +74,14 @@
 
   @Override
   public T set(int index, T element) {
-    // Try to mutate the list first, in case it is immutable
-    T toReturn = backing.set(index, element);
+    T toReturn = workingCopy.set(index, element);
     chain.attach(element, editors.get(index));
     return toReturn;
   }
 
   @Override
   public int size() {
-    return backing.size();
+    return workingCopy.size();
   }
 
   /**
@@ -90,9 +90,9 @@
    * {@link ListEditor#getList()}
    */
   void attach() {
-    editors.addAll(editorSource.create(backing.size(), 0));
-    for (int i = 0, j = backing.size(); i < j; i++) {
-      chain.attach(backing.get(i), editors.get(i));
+    editors.addAll(editorSource.create(workingCopy.size(), 0));
+    for (int i = 0, j = workingCopy.size(); i < j; i++) {
+      chain.attach(workingCopy.get(i), editors.get(i));
     }
   }
 
@@ -104,14 +104,16 @@
   }
 
   void flush() {
-    for (int i = 0, j = backing.size(); i < j; i++) {
+    for (int i = 0, j = workingCopy.size(); i < j; i++) {
       E subEditor = editors.get(i);
       T value = chain.getValue(subEditor);
       // Use of object-identity intentional
-      if (backing.get(i) != value) {
-        backing.set(i, value);
+      if (workingCopy.get(i) != value) {
+        workingCopy.set(i, value);
       }
     }
+    backing.clear();
+    backing.addAll(workingCopy);
   }
 
   /**
diff --git a/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java b/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
index 583c49d..310b9dd 100644
--- a/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
+++ b/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
@@ -15,15 +15,14 @@
  */
 package com.google.gwt.editor.rebind.model;
 
+import com.google.gwt.autobean.shared.ValueCodex;
 import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -32,16 +31,12 @@
  */
 public class ModelUtils {
 
-  @SuppressWarnings("unchecked")
-  static final Set<Class<?>> VALUE_TYPES = Collections.unmodifiableSet(new HashSet<Class<?>>(
-      Arrays.asList(Boolean.class, Character.class, Class.class, Date.class,
-          Enum.class, Number.class, String.class, Void.class)));
-
   static final Set<String> VALUE_TYPE_NAMES;
 
   static {
-    Set<String> names = new HashSet<String>(VALUE_TYPES.size());
-    for (Class<?> clazz : VALUE_TYPES) {
+    Set<Class<?>> valueTypes = ValueCodex.getAllValueTypes();
+    Set<String> names = new HashSet<String>(valueTypes.size());
+    for (Class<?> clazz : valueTypes) {
       names.add(clazz.getName());
     }
     VALUE_TYPE_NAMES = Collections.unmodifiableSet(names);
@@ -110,11 +105,14 @@
     if (classType == null) {
       return true;
     }
+    if (type.isEnum() != null) {
+      return true;
+    }
 
     for (String valueType : VALUE_TYPE_NAMES) {
       JClassType found = oracle.findType(valueType);
       // null check to accommodate limited mock CompilationStates
-      if (found != null && found.isAssignableFrom(classType)) {
+      if (found != null && found.equals(classType)) {
         return true;
       }
     }
diff --git a/user/src/com/google/gwt/event/shared/HandlerManager.java b/user/src/com/google/gwt/event/shared/HandlerManager.java
index 4621491..73d181e 100644
--- a/user/src/com/google/gwt/event/shared/HandlerManager.java
+++ b/user/src/com/google/gwt/event/shared/HandlerManager.java
@@ -19,12 +19,16 @@
 
 /**
  * Manager responsible for adding handlers to event sources and firing those
- * handlers on passed in events. Primitive ancestor of {@link EventBus},
- * and used at the core of {com.google.gwt.user.client.ui.Widget}.
- *
- * @deprecated use {@link SimpleEventBus}.
+ * handlers on passed in events. Primitive ancestor of {@link EventBus}, and
+ * used at the core of {com.google.gwt.user.client.ui.Widget}.
+ * <p>
+ * While widget authors should continue to use
+ * {@link com.google.gwt.user.client.ui.Widget#addDomHandler(EventHandler, com.google.gwt.event.dom.client.DomEvent.Type)}
+ * and
+ * {@link com.google.gwt.user.client.ui.Widget#addHandler(EventHandler, Type)},
+ * application developers are strongly discouraged from using a HandlerManager
+ * instance as a global event dispatch mechanism.
  */
-@Deprecated
 public class HandlerManager implements HasHandlers {
 
   private final SimpleEventBus eventBus;
@@ -50,6 +54,7 @@
    * @param source the event source
    * @param fireInReverseOrder true to fire handlers in reverse order
    */
+  @SuppressWarnings("deprecation")
   public HandlerManager(Object source, boolean fireInReverseOrder) {
     eventBus = new SimpleEventBus(fireInReverseOrder);
     this.source = source;
@@ -114,6 +119,7 @@
    * @param type the handler's event type
    * @return the given handler
    */
+  @SuppressWarnings("deprecation")
   public <H extends EventHandler> H getHandler(GwtEvent.Type<H> type, int index) {
     return eventBus.getHandler(type, index);
   }
@@ -124,6 +130,7 @@
    * @param type the event type
    * @return the number of registered handlers
    */
+  @SuppressWarnings("deprecation")
   public int getHandlerCount(Type<?> type) {
     return eventBus.getHandlerCount(type);
   }
@@ -134,6 +141,7 @@
    * @param e the event type
    * @return whether the given event type is handled
    */
+  @SuppressWarnings("deprecation")
   public boolean isEventHandled(Type<?> e) {
     return eventBus.isEventHandled(e);
   }
diff --git a/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java b/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
index a4d3fb2..6672ac8 100644
--- a/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
+++ b/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
@@ -98,7 +98,7 @@
    * @param timePattern the time pattern String
    * @param datePattern the data pattern String
    */
-  String dateTimeShort(String datePattern, String timePattern);
+  String dateTimeShort(String timePattern, String datePattern);
 
   /**
    * Returns an array of the full era names.
diff --git a/user/src/com/google/gwt/requestfactory/server/LocatorServiceLayer.java b/user/src/com/google/gwt/requestfactory/server/LocatorServiceLayer.java
index a2922e4..d5efc04 100644
--- a/user/src/com/google/gwt/requestfactory/server/LocatorServiceLayer.java
+++ b/user/src/com/google/gwt/requestfactory/server/LocatorServiceLayer.java
@@ -17,12 +17,19 @@
 
 import com.google.gwt.requestfactory.shared.BaseProxy;
 import com.google.gwt.requestfactory.shared.Locator;
-import com.google.gwt.requestfactory.shared.LocatorFor;
-import com.google.gwt.requestfactory.shared.LocatorForName;
+import com.google.gwt.requestfactory.shared.ProxyFor;
+import com.google.gwt.requestfactory.shared.ProxyForName;
+import com.google.gwt.requestfactory.shared.Request;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.requestfactory.shared.ServiceLocator;
+import com.google.gwt.requestfactory.shared.ServiceName;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 
 /**
- * Adds support to the ServiceLayer chain for using {@link Locator} helper
- * objects.
+ * Adds support to the ServiceLayer chain for using {@link Locator} and
+ * {@link ServiceLocator} helper objects.
  */
 final class LocatorServiceLayer extends ServiceLayerDecorator {
 
@@ -37,17 +44,15 @@
 
   @Override
   public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) {
-    Throwable ex;
-    try {
-      return clazz.newInstance();
-    } catch (InstantiationException e) {
-      ex = e;
-    } catch (IllegalAccessException e) {
-      ex = e;
-    }
-    return this.<T> die(ex,
-        "Could not instantiate Locator %s. It is default-instantiable?",
-        clazz.getCanonicalName());
+    return newInstance(clazz, Locator.class);
+  }
+
+  @Override
+  public Object createServiceInstance(Method contextMethod, Method domainMethod) {
+    Class<? extends ServiceLocator> locatorType = getTop().resolveServiceLocator(
+        contextMethod, domainMethod);
+    ServiceLocator locator = newInstance(locatorType, ServiceLocator.class);
+    return locator.getInstance(domainMethod.getDeclaringClass());
   }
 
   @Override
@@ -79,6 +84,17 @@
     return doLoadDomainObject(clazz, domainId);
   }
 
+  /**
+   * Returns true if the context method returns a {@link Request} and the domain
+   * method is non-static.
+   */
+  @Override
+  public boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod) {
+    return Request.class.isAssignableFrom(contextMethod.getReturnType())
+        && !Modifier.isStatic(domainMethod.getModifiers());
+  }
+
   @Override
   public Class<? extends Locator<?, ?>> resolveLocator(Class<?> domainType) {
     // Find the matching BaseProxy
@@ -90,21 +106,24 @@
 
     // Check it for annotations
     Class<? extends Locator<?, ?>> locatorType;
-    LocatorFor l = proxyType.getAnnotation(LocatorFor.class);
-    LocatorForName ln = proxyType.getAnnotation(LocatorForName.class);
-    if (l != null) {
-      locatorType = l.value();
-    } else if (ln != null) {
+    ProxyFor l = proxyType.getAnnotation(ProxyFor.class);
+    ProxyForName ln = proxyType.getAnnotation(ProxyForName.class);
+    if (l != null && !Locator.class.equals(l.locator())) {
+      @SuppressWarnings("unchecked")
+      Class<? extends Locator<?, ?>> found = (Class<? extends Locator<?, ?>>) l.locator();
+      locatorType = found;
+    } else if (ln != null && ln.locator().length() > 0) {
       try {
         @SuppressWarnings("unchecked")
         Class<? extends Locator<?, ?>> found = (Class<? extends Locator<?, ?>>) Class.forName(
-            ln.value(), false, domainType.getClassLoader()).asSubclass(
+            ln.locator(), false, domainType.getClassLoader()).asSubclass(
             Locator.class);
         locatorType = found;
       } catch (ClassNotFoundException e) {
-        return die(e,
-            "Could not find the type specified in the @%s annotation %s",
-            LocatorForName.class.getCanonicalName(), ln.value());
+        return die(
+            e,
+            "Could not find the locator type specified in the @%s annotation %s",
+            ProxyForName.class.getCanonicalName(), ln.value());
       }
     } else {
       // No locator annotation
@@ -113,6 +132,34 @@
     return locatorType;
   }
 
+  @Override
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod) {
+    Class<? extends ServiceLocator> locatorType;
+
+    // Look at the RequestContext
+    Class<?> requestContextClass = contextMethod.getDeclaringClass();
+    Service l = requestContextClass.getAnnotation(Service.class);
+    ServiceName ln = requestContextClass.getAnnotation(ServiceName.class);
+    if (l != null && !ServiceLocator.class.equals(l.locator())) {
+      locatorType = l.locator();
+    } else if (ln != null && ln.locator().length() > 0) {
+      try {
+        locatorType = Class.forName(ln.locator(), false,
+            requestContextClass.getClassLoader()).asSubclass(
+            ServiceLocator.class);
+      } catch (ClassNotFoundException e) {
+        return die(
+            e,
+            "Could not find the locator type specified in the @%s annotation %s",
+            ServiceName.class.getCanonicalName(), ln.value());
+      }
+    } else {
+      locatorType = null;
+    }
+    return locatorType;
+  }
+
   private <T> Object doGetId(T domainObject) {
     @SuppressWarnings("unchecked")
     Class<T> clazz = (Class<T>) domainObject.getClass();
@@ -162,4 +209,18 @@
     }
     return (Locator<T, I>) getTop().createLocator(locatorType);
   }
+
+  private <T> T newInstance(Class<T> clazz, Class<? super T> base) {
+    Throwable ex;
+    try {
+      return clazz.newInstance();
+    } catch (InstantiationException e) {
+      ex = e;
+    } catch (IllegalAccessException e) {
+      ex = e;
+    }
+    return this.<T> die(ex,
+        "Could not instantiate %s %s. Is it default-instantiable?",
+        base.getSimpleName(), clazz.getCanonicalName());
+  }
 }
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java b/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
index 5dafce4..f034b1c 100644
--- a/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
+++ b/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.requestfactory.server;
 
+import com.google.gwt.autobean.shared.ValueCodex;
 import com.google.gwt.dev.asm.AnnotationVisitor;
 import com.google.gwt.dev.asm.ClassReader;
 import com.google.gwt.dev.asm.ClassVisitor;
@@ -31,8 +32,6 @@
 import com.google.gwt.requestfactory.shared.BaseProxy;
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.InstanceRequest;
-import com.google.gwt.requestfactory.shared.LocatorFor;
-import com.google.gwt.requestfactory.shared.LocatorForName;
 import com.google.gwt.requestfactory.shared.ProxyFor;
 import com.google.gwt.requestfactory.shared.ProxyForName;
 import com.google.gwt.requestfactory.shared.Request;
@@ -44,10 +43,9 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -142,6 +140,7 @@
   private class DomainMapper extends EmptyVisitor {
     private final ErrorContext logger;
     private String domainInternalName;
+    private List<Class<? extends Annotation>> found = new ArrayList<Class<? extends Annotation>>();
     private String locatorInternalName;
 
     public DomainMapper(ErrorContext logger) {
@@ -165,65 +164,93 @@
       }
     }
 
+    /**
+     * This method examines one annotation at a time.
+     */
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      final boolean foundLocator = desc.equals(Type.getDescriptor(LocatorFor.class));
-      final boolean foundLocatorName = desc.equals(Type.getDescriptor(LocatorForName.class));
-      boolean foundProxy = desc.equals(Type.getDescriptor(ProxyFor.class));
-      boolean foundProxyName = desc.equals(Type.getDescriptor(ProxyForName.class));
-      boolean foundService = desc.equals(Type.getDescriptor(Service.class));
-      boolean foundServiceName = desc.equals(Type.getDescriptor(ServiceName.class));
+      // Set to true if the annotation should have class literal values
+      boolean expectClasses = false;
+      // Set to true if the annonation has string values
+      boolean expectNames = false;
 
-      if (foundLocator || foundProxy || foundService) {
+      if (desc.equals(Type.getDescriptor(ProxyFor.class))) {
+        expectClasses = true;
+        found.add(ProxyFor.class);
+      } else if (desc.equals(Type.getDescriptor(ProxyForName.class))) {
+        expectNames = true;
+        found.add(ProxyForName.class);
+      } else if (desc.equals(Type.getDescriptor(Service.class))) {
+        expectClasses = true;
+        found.add(Service.class);
+      } else if (desc.equals(Type.getDescriptor(ServiceName.class))) {
+        expectNames = true;
+        found.add(ServiceName.class);
+      }
+
+      if (expectClasses) {
         return new EmptyVisitor() {
           @Override
           public void visit(String name, Object value) {
             if ("value".equals(name)) {
-              String found = ((Type) value).getInternalName();
-              if (foundLocator) {
-                locatorInternalName = found;
-              } else {
-                domainInternalName = found;
-              }
+              domainInternalName = ((Type) value).getInternalName();
+            } else if ("locator".equals(name)) {
+              locatorInternalName = ((Type) value).getInternalName();
             }
           }
         };
       }
 
-      if (foundLocatorName || foundProxyName || foundServiceName) {
+      if (expectNames) {
         return new EmptyVisitor() {
           @Override
           public void visit(String name, Object value) {
-            if ("value".equals(name)) {
-              String sourceName = (String) value;
-
-              /*
-               * The input is a source name, so we need to convert it to an
-               * internal name. We'll do this by substituting dollar signs for
-               * the last slash in the name until there are no more slashes.
-               */
-              StringBuffer desc = new StringBuffer(sourceName.replace('.', '/'));
-              while (!loader.exists(desc.toString() + ".class")) {
-                logger.spam("Did not find " + desc.toString());
-                int idx = desc.lastIndexOf("/");
-                if (idx == -1) {
-                  return;
-                }
-                desc.setCharAt(idx, '$');
-              }
-
-              if (foundLocatorName) {
-                locatorInternalName = desc.toString();
-              } else {
-                domainInternalName = desc.toString();
-              }
-              logger.spam(domainInternalName);
+            String sourceName;
+            if ("value".equals(name) || "locator".equals(name)) {
+              sourceName = (String) value;
+            } else {
+              return;
             }
+
+            /*
+             * The input is a source name, so we need to convert it to an
+             * internal name. We'll do this by substituting dollar signs for the
+             * last slash in the name until there are no more slashes.
+             */
+            StringBuffer desc = new StringBuffer(sourceName.replace('.', '/'));
+            while (!loader.exists(desc.toString() + ".class")) {
+              logger.spam("Did not find " + desc.toString());
+              int idx = desc.lastIndexOf("/");
+              if (idx == -1) {
+                return;
+              }
+              desc.setCharAt(idx, '$');
+            }
+
+            if ("locator".equals(name)) {
+              locatorInternalName = desc.toString();
+            } else {
+              domainInternalName = desc.toString();
+            }
+            logger.spam(domainInternalName);
           }
         };
       }
       return null;
     }
+
+    @Override
+    public void visitEnd() {
+      // Only allow one annotation
+      if (found.size() > 1) {
+        StringBuilder sb = new StringBuilder();
+        for (Class<?> clazz : found) {
+          sb.append(" @").append(clazz.getSimpleName());
+        }
+        logger.poison("Redundant domain mapping annotations present:%s",
+            sb.toString());
+      }
+    }
   }
 
   /**
@@ -489,10 +516,7 @@
     }
   }
 
-  @SuppressWarnings("unchecked")
-  static final Set<Class<?>> VALUE_TYPES = Collections.unmodifiableSet(new HashSet<Class<?>>(
-      Arrays.asList(Boolean.class, Character.class, Class.class, Date.class,
-          Enum.class, Number.class, String.class, Void.class)));
+  static final Set<Class<?>> VALUE_TYPES = ValueCodex.getAllValueTypes();
 
   public static void main(String[] args) {
     if (args.length == 0) {
@@ -536,10 +560,10 @@
    */
   private final Map<Type, Type> clientToDomainType = new HashMap<Type, Type>();
   /**
-   * Maps client types (e.g. FooProxy) to their locator types (e.g. FooLocator).
+   * Maps client types (e.g. FooProxy or FooContext) to their locator types
+   * (e.g. FooLocator or FooServiceLocator).
    */
   private final Map<Type, Type> clientToLocatorMap = new HashMap<Type, Type>();
-
   /**
    * Maps domain types (e.g Foo) to client proxy types (e.g. FooAProxy,
    * FooBProxy).
@@ -550,6 +574,10 @@
    */
   private final Type entityProxyIntf = Type.getType(EntityProxy.class);
   /**
+   * The type {@link Enum}.
+   */
+  private final Type enumType = Type.getType(Enum.class);
+  /**
    * A placeholder type for client types that could not be resolved to a domain
    * type.
    */
@@ -731,7 +759,8 @@
       }
 
       // Check the client method against the domain
-      checkClientMethodInDomain(logger, method, domainServiceType);
+      checkClientMethodInDomain(logger, method, domainServiceType,
+          !clientToLocatorMap.containsKey(requestContextType));
       maybeCheckReferredProxies(logger, method);
     }
 
@@ -887,7 +916,7 @@
    * to the server's domain type.
    */
   private void checkClientMethodInDomain(ErrorContext logger, RFMethod method,
-      Type domainServiceType) {
+      Type domainServiceType, boolean requireStaticMethodsForRequestType) {
     logger = logger.setMethod(method);
 
     // Create a "translated" method declaration to search for
@@ -905,7 +934,8 @@
         logger.poison("The method %s is declared to return %s, but the"
             + " service method is static", method.getName(),
             InstanceRequest.class.getCanonicalName());
-      } else if (!isInstance && !found.isDeclaredStatic) {
+      } else if (requireStaticMethodsForRequestType && !isInstance
+          && !found.isDeclaredStatic()) {
         logger.poison("The method %s is declared to return %s, but the"
             + " service method is not static", method.getName(),
             Request.class.getCanonicalName());
@@ -1331,12 +1361,8 @@
       return true;
     }
     logger = logger.setType(type);
-    List<Type> types = getSupertypes(logger, type);
-    for (Type t : types) {
-      if (valueTypes.contains(t)) {
-        valueTypes.add(type);
-        return true;
-      }
+    if (isAssignable(logger, enumType, type)) {
+      return true;
     }
     return false;
   }
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java b/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
index c0e1418..4ad67c1 100644
--- a/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
+++ b/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
@@ -130,7 +130,6 @@
       if (DUMP_PAYLOAD) {
         System.out.println(">>> " + jsonRequestString);
       }
-      PrintWriter writer = response.getWriter();
 
       try {
         // Check that user is logged in before proceeding
@@ -146,6 +145,8 @@
           response.setHeader("userId", String.format("%s", userInfo.getId()));
           response.setStatus(HttpServletResponse.SC_OK);
           response.setContentType(RequestFactory.JSON_CONTENT_TYPE_UTF8);
+          // The Writer must be obtained after setting the content type
+          PrintWriter writer = response.getWriter();
           writer.print(payload);
           writer.flush();
         }
diff --git a/user/src/com/google/gwt/requestfactory/server/ServiceLayer.java b/user/src/com/google/gwt/requestfactory/server/ServiceLayer.java
index a906e9f..8cd93a4 100644
--- a/user/src/com/google/gwt/requestfactory/server/ServiceLayer.java
+++ b/user/src/com/google/gwt/requestfactory/server/ServiceLayer.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.requestfactory.shared.BaseProxy;
 import com.google.gwt.requestfactory.shared.Locator;
+import com.google.gwt.requestfactory.shared.ServiceLocator;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
@@ -109,6 +110,17 @@
   public abstract <T extends Locator<?, ?>> T createLocator(Class<T> clazz);
 
   /**
+   * Create an instance of a service object that can be used as the target for
+   * the given method invocation.
+   * 
+   * @param contextMethod a method defined in a RequestContext
+   * @param domainMethod the method that the service object must implement
+   * @return an instance of the requested service object
+   */
+  public abstract Object createServiceInstance(Method contextMethod,
+      Method domainMethod);
+
+  /**
    * Return the persistent id for a domain object. May return {@code null} to
    * indicate that the domain object has not been persisted. The value returned
    * from this method must be a simple type (e.g. Integer, String) or a domain
@@ -209,6 +221,18 @@
       List<Object> domainIds);
 
   /**
+   * Determines if the invocation of a domain method requires a
+   * {@link ServiceLocator} as the 0th parameter when passed into
+   * {@link #invoke(Method, Object...)}.
+   * 
+   * @param contextMethod a method defined in a RequestContext
+   * @param domainMethod a domain method
+   * @return {@code true} if a ServiceLocator is required
+   */
+  public abstract boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod);
+
+  /**
    * Given a type token previously returned from
    * {@link #resolveTypeToken(Class)}, return the Class literal associated with
    * the token.
@@ -278,6 +302,19 @@
       String requestContextClass, String methodName);
 
   /**
+   * Given a RequestContext method declaration, resolve the
+   * {@link ServiceLocator} that should be used when invoking the domain method.
+   * This method will only be called if {@link #requiresServiceLocator(Method)}
+   * returned {@code true} for the associated domain method.
+   * 
+   * @param contextMethod a RequestContext method declaration
+   * @param domainMethod the domain method that will be invoked
+   * @return the type of ServiceLocator to use
+   */
+  public abstract Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod);
+
+  /**
    * Return a string used to represent the given type in the wire protocol.
    * 
    * @param proxyType a client-side EntityProxy or ValueProxy type
diff --git a/user/src/com/google/gwt/requestfactory/server/ServiceLayerCache.java b/user/src/com/google/gwt/requestfactory/server/ServiceLayerCache.java
index 887d49c..0c89f75 100644
--- a/user/src/com/google/gwt/requestfactory/server/ServiceLayerCache.java
+++ b/user/src/com/google/gwt/requestfactory/server/ServiceLayerCache.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.requestfactory.shared.BaseProxy;
 import com.google.gwt.requestfactory.shared.Locator;
+import com.google.gwt.requestfactory.shared.ServiceLocator;
 import com.google.gwt.rpc.server.Pair;
 
 import java.lang.ref.SoftReference;
@@ -42,20 +43,27 @@
   private static SoftReference<Map<Method, Map<Object, Object>>> methodCache;
 
   private static final Method createLocator;
+  private static final Method createServiceInstance;
   private static final Method getIdType;
   private static final Method getRequestReturnType;
+  private static final Method requiresServiceLocator;
   private static final Method resolveClass;
   private static final Method resolveClientType;
   private static final Method resolveDomainClass;
   private static final Method resolveDomainMethod;
   private static final Method resolveLocator;
   private static final Method resolveRequestContextMethod;
+  private static final Method resolveServiceLocator;
   private static final Method resolveTypeToken;
 
   static {
     createLocator = getMethod("createLocator", Class.class);
+    createServiceInstance = getMethod("createServiceInstance", Method.class,
+        Method.class);
     getIdType = getMethod("getIdType", Class.class);
     getRequestReturnType = getMethod("getRequestReturnType", Method.class);
+    requiresServiceLocator = getMethod("requiresServiceLocator", Method.class,
+        Method.class);
     resolveClass = getMethod("resolveClass", String.class);
     resolveClientType = getMethod("resolveClientType", Class.class,
         Class.class, boolean.class);
@@ -64,6 +72,8 @@
     resolveLocator = getMethod("resolveLocator", Class.class);
     resolveRequestContextMethod = getMethod("resolveRequestContextMethod",
         String.class, String.class);
+    resolveServiceLocator = getMethod("resolveServiceLocator", Method.class,
+        Method.class);
     resolveTypeToken = getMethod("resolveTypeToken", Class.class);
   }
 
@@ -98,6 +108,12 @@
   }
 
   @Override
+  public Object createServiceInstance(Method contextMethod, Method domainMethod) {
+    return getOrCache(createServiceInstance, new Pair<Method, Method>(
+        contextMethod, domainMethod), Object.class, contextMethod, domainMethod);
+  }
+
+  @Override
   public Class<?> getIdType(Class<?> domainType) {
     return getOrCache(getIdType, domainType, Class.class, domainType);
   }
@@ -109,18 +125,26 @@
   }
 
   @Override
+  public boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod) {
+    return getOrCache(requiresServiceLocator, new Pair<Method, Method>(
+        contextMethod, domainMethod), Boolean.class, contextMethod,
+        domainMethod);
+  }
+
+  @Override
   public Class<? extends BaseProxy> resolveClass(String typeToken) {
     Class<?> found = getOrCache(resolveClass, typeToken, Class.class, typeToken);
     return found.asSubclass(BaseProxy.class);
   }
 
   @Override
-  @SuppressWarnings("unchecked")
   public <T> Class<? extends T> resolveClientType(Class<?> domainClass,
       Class<T> clientType, boolean required) {
-    return getOrCache(resolveClientType, new Pair<Class<?>, Class<?>>(
-        domainClass, clientType), Class.class, domainClass, clientType,
-        required);
+    Class<?> clazz = getOrCache(resolveClientType,
+        new Pair<Class<?>, Class<?>>(domainClass, clientType), Class.class,
+        domainClass, clientType, required);
+    return clazz == null ? null : clazz.asSubclass(clientType);
   }
 
   @Override
@@ -149,6 +173,15 @@
   }
 
   @Override
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod) {
+    Class<?> clazz = getOrCache(resolveServiceLocator,
+        new Pair<Method, Method>(contextMethod, domainMethod), Class.class,
+        contextMethod, domainMethod);
+    return clazz == null ? null : clazz.asSubclass(ServiceLocator.class);
+  }
+
+  @Override
   public String resolveTypeToken(Class<? extends BaseProxy> domainClass) {
     return getOrCache(resolveTypeToken, domainClass, String.class, domainClass);
   }
diff --git a/user/src/com/google/gwt/requestfactory/server/ServiceLayerDecorator.java b/user/src/com/google/gwt/requestfactory/server/ServiceLayerDecorator.java
index b9a2248..c642d94 100644
--- a/user/src/com/google/gwt/requestfactory/server/ServiceLayerDecorator.java
+++ b/user/src/com/google/gwt/requestfactory/server/ServiceLayerDecorator.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.requestfactory.shared.BaseProxy;
 import com.google.gwt.requestfactory.shared.Locator;
+import com.google.gwt.requestfactory.shared.ServiceLocator;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -54,6 +55,11 @@
   }
 
   @Override
+  public Object createServiceInstance(Method contextMethod, Method domainMethod) {
+    return getNext().createServiceInstance(contextMethod, domainMethod);
+  }
+
+  @Override
   public Object getId(Object domainObject) {
     return getNext().getId(domainObject);
   }
@@ -100,6 +106,12 @@
   }
 
   @Override
+  public boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod) {
+    return getNext().requiresServiceLocator(contextMethod, domainMethod);
+  }
+
+  @Override
   public Class<? extends BaseProxy> resolveClass(String typeToken) {
     return getNext().resolveClass(typeToken);
   }
@@ -133,6 +145,12 @@
   }
 
   @Override
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod) {
+    return getNext().resolveServiceLocator(contextMethod, domainMethod);
+  }
+
+  @Override
   public String resolveTypeToken(Class<? extends BaseProxy> proxyType) {
     return getNext().resolveTypeToken(proxyType);
   }
diff --git a/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java
index 4be0769..07fe1f5 100644
--- a/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java
+++ b/user/src/com/google/gwt/requestfactory/server/SimpleRequestProcessor.java
@@ -27,6 +27,7 @@
 import com.google.gwt.requestfactory.shared.BaseProxy;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.requestfactory.shared.InstanceRequest;
+import com.google.gwt.requestfactory.shared.Request;
 import com.google.gwt.requestfactory.shared.ServerFailure;
 import com.google.gwt.requestfactory.shared.WriteOperation;
 import com.google.gwt.requestfactory.shared.impl.BaseProxyCategory;
@@ -47,7 +48,6 @@
 
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -346,7 +346,7 @@
    */
   private List<Object> decodeInvocationArguments(RequestState source,
       InvocationMessage invocation, Method contextMethod, Method domainMethod) {
-    boolean isStatic = Modifier.isStatic(domainMethod.getModifiers());
+    boolean isStatic = Request.class.isAssignableFrom(contextMethod.getReturnType());
     int baseLength = contextMethod.getParameterTypes().length;
     int length = baseLength + (isStatic ? 0 : 1);
     int offset = isStatic ? 0 : 1;
@@ -418,9 +418,16 @@
               + invocation.getOperation(), null);
         }
 
-        // Invoke it
+        // Compute the arguments
         List<Object> args = decodeInvocationArguments(state, invocation,
             contextMethod, domainMethod);
+        // Possibly use a ServiceLocator
+        if (service.requiresServiceLocator(contextMethod, domainMethod)) {
+          Object serviceInstance = service.createServiceInstance(contextMethod,
+              domainMethod);
+          args.add(0, serviceInstance);
+        }
+        // Invoke it
         Object returnValue = service.invoke(domainMethod, args.toArray());
 
         // Convert domain object to client object
diff --git a/user/src/com/google/gwt/requestfactory/shared/Locator.java b/user/src/com/google/gwt/requestfactory/shared/Locator.java
index a88032b..3516984 100644
--- a/user/src/com/google/gwt/requestfactory/shared/Locator.java
+++ b/user/src/com/google/gwt/requestfactory/shared/Locator.java
@@ -29,7 +29,7 @@
  * @param <T> the type of domain object the Locator will operate on
  * @param <I> the type of object the Locator expects to use as an id for the
  *          domain object
- * @see LocatorFor
+ * @see ProxyFor#locator()
  */
 public abstract class Locator<T, I> {
   /**
diff --git a/user/src/com/google/gwt/requestfactory/shared/LocatorFor.java b/user/src/com/google/gwt/requestfactory/shared/LocatorFor.java
deleted file mode 100644
index 2c237fd..0000000
--- a/user/src/com/google/gwt/requestfactory/shared/LocatorFor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2010 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.requestfactory.shared;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation on BaseProxy classes specifying the domain (server-side) object
- * {@link Locator}. Specifying an explicit {@code Locator} removes the
- * requirement to have a {@code findFoo()}, {@code getId()}, and
- * {@code getVersion()} method in the domain object type.
- * 
- * @see LocatorForName
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface LocatorFor {
-  Class<? extends Locator<?, ?>> value();
-}
diff --git a/user/src/com/google/gwt/requestfactory/shared/LocatorForName.java b/user/src/com/google/gwt/requestfactory/shared/LocatorForName.java
deleted file mode 100644
index f537cf4..0000000
--- a/user/src/com/google/gwt/requestfactory/shared/LocatorForName.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2010 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.requestfactory.shared;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation on BaseProxy classes specifying the domain (server-side) object
- * {@link Locator}. This annotation can be used in place of {@link LocatorFor}
- * if the domain Locator is not available to the GWT compiler or DevMode
- * runtime.
- * 
- * @see LocatorFor
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface LocatorForName {
-  String value();
-}
diff --git a/user/src/com/google/gwt/requestfactory/shared/ProxyFor.java b/user/src/com/google/gwt/requestfactory/shared/ProxyFor.java
index 4da1afe..38f6df6 100644
--- a/user/src/com/google/gwt/requestfactory/shared/ProxyFor.java
+++ b/user/src/com/google/gwt/requestfactory/shared/ProxyFor.java
@@ -21,13 +21,22 @@
 import java.lang.annotation.Target;
 
 /**
- * Annotation on EntityProxy classes specifying the domain (server-side) object
- * type.
+ * Annotation on EntityProxy and ValueProxy classes specifying the domain
+ * (server-side) object type.
  * 
  * @see ProxyForName
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface ProxyFor {
+  /**
+   * The domain type that the proxy is mapped to.
+   */
   Class<?> value();
+
+  /**
+   * An optional {@link Locator} that provides instances of the domain objects.
+   */
+  @SuppressWarnings("rawtypes")
+  Class<? extends Locator> locator() default Locator.class;
 }
diff --git a/user/src/com/google/gwt/requestfactory/shared/ProxyForName.java b/user/src/com/google/gwt/requestfactory/shared/ProxyForName.java
index fa3f5bd..5eb5594 100644
--- a/user/src/com/google/gwt/requestfactory/shared/ProxyForName.java
+++ b/user/src/com/google/gwt/requestfactory/shared/ProxyForName.java
@@ -28,5 +28,14 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface ProxyForName {
+  /**
+   * The name of the domain type that the proxy is mapped to.
+   */
   String value();
+
+  /**
+   * An optional name of a {@link Locator} that provides instances of the domain
+   * objects.
+   */
+  String locator() default "";
 }
diff --git a/user/src/com/google/gwt/requestfactory/shared/Service.java b/user/src/com/google/gwt/requestfactory/shared/Service.java
index 29d8829..de14983 100644
--- a/user/src/com/google/gwt/requestfactory/shared/Service.java
+++ b/user/src/com/google/gwt/requestfactory/shared/Service.java
@@ -29,5 +29,16 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface Service {
+  /**
+   * The domain type that provides the implementations for the methods defined
+   * in the RequestContext.
+   */
   Class<?> value();
+
+  /**
+   * An optional {@link ServiceLocator} that provides instances of service
+   * objects used when invoking instance methods on the type returned by
+   * {@link #value()}.
+   */
+  Class<? extends ServiceLocator> locator() default ServiceLocator.class;
 }
diff --git a/user/src/com/google/gwt/requestfactory/shared/ServiceLocator.java b/user/src/com/google/gwt/requestfactory/shared/ServiceLocator.java
new file mode 100644
index 0000000..dfa841d
--- /dev/null
+++ b/user/src/com/google/gwt/requestfactory/shared/ServiceLocator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 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.requestfactory.shared;
+
+/**
+ * A ServiceLocator provides instances of a type specified by a {@link Service}
+ * when {@link Request} methods declared in a {@link RequestContext}are mapped
+ * onto instance (non-static) methods.
+ * <p>
+ * ServiceLocator subtypes must be default instantiable (i.e. public static
+ * types with a no-arg constructor). Instances of ServiceLocators may be
+ * retained and reused by the RequestFactory service layer.
+ * 
+ * @see Service#locator()
+ */
+public interface ServiceLocator {
+  /**
+   * Returns an instance of the service object.
+   * 
+   * @param clazz the requested type of service object
+   * @return an instance of the service object
+   */
+  Object getInstance(Class<?> clazz);
+}
diff --git a/user/src/com/google/gwt/requestfactory/shared/ServiceName.java b/user/src/com/google/gwt/requestfactory/shared/ServiceName.java
index beb72b6..464c032 100644
--- a/user/src/com/google/gwt/requestfactory/shared/ServiceName.java
+++ b/user/src/com/google/gwt/requestfactory/shared/ServiceName.java
@@ -28,5 +28,16 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface ServiceName {
+  /**
+   * The binary name of the domain type that provides the implementations for
+   * the methods defined in the RequestContext.
+   */
   String value();
+
+  /**
+   * An optional binary name of a {@link ServiceLocator} that provides instances
+   * of service objects used when invoking instance methods on the type returned
+   * by {@link #value()}.
+   */
+  String locator() default "";
 }
diff --git a/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java b/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java
index 2a45831..839f4ee 100644
--- a/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java
+++ b/user/src/com/google/gwt/requestfactory/shared/impl/AbstractRequestContext.java
@@ -434,7 +434,10 @@
             if (properties.containsKey(propertyName)) {
               Splittable raw = properties.get(propertyName);
               Object decoded = ValueCodex.decode(ctx.getType(), raw);
-              // Hack for Date, consider generalizing for "custom serializers"
+              /*
+               * Hack for Date subtypes, consider generalizing for
+               * "custom serializers"
+               */
               if (decoded != null && Date.class.equals(ctx.getType())) {
                 decoded = new DatePoser((Date) decoded);
               }
diff --git a/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java b/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
index 9a8699b..78af84d 100644
--- a/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
+++ b/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
@@ -47,6 +47,7 @@
    * Create an instance with the given format and time zone.
    */
   public DateTimeFormatRenderer(DateTimeFormat format, TimeZone timeZone) {
+    assert format != null;
     this.format = format;
     this.timeZone = timeZone;
   }
diff --git a/user/src/com/google/gwt/text/client/DoubleParser.java b/user/src/com/google/gwt/text/client/DoubleParser.java
index c3977dd..dca4195 100644
--- a/user/src/com/google/gwt/text/client/DoubleParser.java
+++ b/user/src/com/google/gwt/text/client/DoubleParser.java
@@ -46,7 +46,7 @@
     }
 
     try {
-      return Math.rint(NumberFormat.getDecimalFormat().parse(object.toString()));
+      return NumberFormat.getDecimalFormat().parse(object.toString());
     } catch (NumberFormatException e) {
       throw new ParseException(e.getMessage(), 0);
     }
diff --git a/user/src/com/google/gwt/text/client/NumberFormatRenderer.java b/user/src/com/google/gwt/text/client/NumberFormatRenderer.java
new file mode 100644
index 0000000..b6e1e88
--- /dev/null
+++ b/user/src/com/google/gwt/text/client/NumberFormatRenderer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 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.text.client;
+
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.text.shared.AbstractRenderer;
+
+/**
+ * Renders {@link Number} objects with a {@link NumberFormat}.
+ */
+public class NumberFormatRenderer extends AbstractRenderer<Number> {
+  private final NumberFormat format;
+
+  /**
+   * Create an instance using {@link NumberFormat#getDecimalFormat()}.
+   */
+  public NumberFormatRenderer() {
+    this(NumberFormat.getDecimalFormat());
+  }
+
+  /**
+   * Create an instance with the given format.
+   */
+  public NumberFormatRenderer(NumberFormat format) {
+    this.format = format;
+  }
+
+  public String render(Number object) {
+    if (object == null) {
+      return "";
+    }
+    return format.format(object);
+  }
+}
diff --git a/user/src/com/google/gwt/uibinder/client/UiChild.java b/user/src/com/google/gwt/uibinder/client/UiChild.java
index 3f11958..d9bca54 100644
--- a/user/src/com/google/gwt/uibinder/client/UiChild.java
+++ b/user/src/com/google/gwt/uibinder/client/UiChild.java
@@ -42,14 +42,15 @@
  * 
  * &#064;UiChild MyWidget#addCustomChild(Widget w) </code> and
  * 
- *          <pre>
- *          <p:MyWidget>
- *          <p:customchild>
- *          <g:SomeWidget />
- *          </p:customchild>
- *          </p:MyWidget>
- *          </pre> would invoke the <code>addCustomChild</code> function to add
- *          an instance of SomeWidget.
+ * <pre>
+ *   &lt;p:MyWidget>
+ *     &lt;p:customchild>
+ *       &lt;g:SomeWidget />
+ *     &lt;/p:customchild>
+ *   &lt;/p:MyWidget>
+ * </pre> 
+ * would invoke the <code>addCustomChild</code> function to add an instance of
+ * SomeWidget.
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java
new file mode 100644
index 0000000..1359664
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2010 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.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.TypeOracleException;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.TimeZone;
+import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.XMLElement;
+
+/**
+ * Parses {@link com.google.gwt.user.client.ui.DateLabel} widgets.
+ */
+public class DateLabelParser implements ElementParser {
+
+  static final String AT_MOST_ONE_SPECIFIED_FORMAT = "May have at most one of format, predefinedFormat and customFormat.";
+  static final String AT_MOST_ONE_SPECIFIED_TIME_ZONE = "May have at most one of timezone and timezoneOffset.";
+  static final String NO_TIMEZONE_WITHOUT_SPECIFIED_FORMAT = "May not specify a time zone if no format is given.";
+
+  public void parse(XMLElement elem, String fieldName, JClassType type,
+      UiBinderWriter writer) throws UnableToCompleteException {
+    boolean supportsTimeZone = hasDateTimeFormatAndTimeZoneConstructor(
+        writer.getOracle(), type);
+    if (hasDateTimeFormatConstructor(writer.getOracle(), type)
+        || supportsTimeZone) {
+      String format = consumeFormat(elem, writer);
+
+      if (format != null) {
+        String timeZone = (supportsTimeZone ? consumeTimeZone(elem, writer)
+            : null);
+
+        writer.setFieldInitializerAsConstructor(fieldName, type, makeArgs(
+            format, timeZone));
+      } else if (supportsTimeZone && hasTimeZone(elem)) {
+        writer.die(elem, NO_TIMEZONE_WITHOUT_SPECIFIED_FORMAT);
+      }
+    }
+  }
+
+  private String consumeFormat(XMLElement elem, UiBinderWriter writer)
+      throws UnableToCompleteException {
+    String format = elem.consumeAttribute("format",
+        writer.getOracle().findType(DateTimeFormat.class.getCanonicalName()));
+    String predefinedFormat = elem.consumeAttribute("predefinedFormat",
+        writer.getOracle().findType(PredefinedFormat.class.getCanonicalName()));
+    String customFormat = elem.consumeStringAttribute("customFormat");
+
+    if (format != null) {
+      if (predefinedFormat != null || customFormat != null) {
+        writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT);
+      }
+      return format;
+    }
+    if (predefinedFormat != null) {
+      if (customFormat != null) {
+        writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT);
+      }
+      return makeGetFormat(predefinedFormat);
+    }
+    if (customFormat != null) {
+      return makeGetFormat(customFormat);
+    }
+    return null;
+  }
+
+  private String consumeTimeZone(XMLElement elem, UiBinderWriter writer)
+      throws UnableToCompleteException {
+    String timeZone = elem.consumeAttribute("timezone",
+        writer.getOracle().findType(TimeZone.class.getCanonicalName()));
+    String timeZoneOffset = elem.consumeAttribute("timezoneOffset",
+        getIntType(writer.getOracle()));
+    if (timeZone != null && timeZoneOffset != null) {
+      writer.die(elem, AT_MOST_ONE_SPECIFIED_TIME_ZONE);
+    }
+    if (timeZone != null) {
+      return timeZone;
+    }
+    if (timeZoneOffset != null) {
+      return TimeZone.class.getCanonicalName() + ".createTimeZone("
+          + timeZoneOffset + ")";
+    }
+    return null;
+  }
+
+  private JType getIntType(TypeOracle oracle) {
+    try {
+      return oracle.parse("int");
+    } catch (TypeOracleException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private boolean hasDateTimeFormatAndTimeZoneConstructor(
+      TypeOracle typeOracle, JClassType type) {
+    JType dateTimeFormatType = typeOracle.findType(DateTimeFormat.class.getName());
+    JType timeZoneType = typeOracle.findType(TimeZone.class.getName());
+    return type.findConstructor(new JType[] {dateTimeFormatType, timeZoneType}) != null;
+  }
+
+  private boolean hasDateTimeFormatConstructor(TypeOracle typeOracle,
+      JClassType type) {
+    JType dateTimeFormatType = typeOracle.findType(DateTimeFormat.class.getName());
+    return type.findConstructor(new JType[] {dateTimeFormatType}) != null;
+  }
+
+  private boolean hasTimeZone(XMLElement elem) {
+    return elem.hasAttribute("timezone") || elem.hasAttribute("timezoneOffset");
+  }
+
+  private String[] makeArgs(String format, String timeZone) {
+    if (timeZone == null) {
+      return new String[] {format};
+    }
+    return new String[] {format, timeZone};
+  }
+
+  private String makeGetFormat(String format) {
+    return DateTimeFormat.class.getCanonicalName() + ".getFormat(" + format
+        + ")";
+  }
+}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/NumberLabelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/NumberLabelParser.java
new file mode 100644
index 0000000..5ff6656
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/elementparsers/NumberLabelParser.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2010 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.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.i18n.client.CurrencyData;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.XMLElement;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Parses {@link com.google.gwt.user.client.ui.DateLabel} widgets.
+ */
+public class NumberLabelParser implements ElementParser {
+
+  static final String AT_MOST_ONE_SPECIFIED_FORMAT = "May have only one of format, predefinedFormat and customFormat.";
+  static final String AT_MOST_ONE_SPECIFIED_CURRENCY = "May have only one of currencyData and customCode.";
+  static final String NO_CURRENCY_WITH_FORMAT = "May not specify both a NumberFormat and a currency code.";
+  static final String NO_CURRENCY_WITHOUT_SPECIFIED_FORMAT = "May not specify a currency code if no format is given.";
+  static final String NO_CURRENCY_WITH_PREDEFINED_FORMAT = "May not specify a currency code with a predefined format (except the CURRENCY format)";
+  static final String UNKNOWN_PREDEFINED_FORMAT = "Unknown predefined format: %s";
+
+  private static final Map<String, String> predefinedFormats;
+
+  static {
+    String prefix = NumberFormat.class.getCanonicalName();
+    Map<String, String> formats = new HashMap<String, String>(4);
+    formats.put("DECIMAL", prefix + ".getDecimalFormat()");
+    formats.put("PERCENT", prefix + ".getPercentFormat()");
+    formats.put("SCIENTIFIC", prefix + ".getScientificFormat()");
+    // CURRENCY is special-cased in consumeFormat.
+    predefinedFormats = Collections.unmodifiableMap(formats);
+  }
+
+  public void parse(XMLElement elem, String fieldName, JClassType type,
+      UiBinderWriter writer) throws UnableToCompleteException {
+    if (hasNumberFormatConstructor(writer.getOracle(), type)) {
+      String format = consumeFormat(elem, writer);
+
+      if (format != null) {
+        writer.setFieldInitializerAsConstructor(fieldName, type, format);
+      }
+    }
+  }
+
+  private String consumeCurrency(XMLElement elem, UiBinderWriter writer)
+      throws UnableToCompleteException {
+    String currencyData = elem.consumeAttribute("currencyData",
+        writer.getOracle().findType(CurrencyData.class.getCanonicalName()));
+    String currencyCode = elem.consumeStringAttribute("currencyCode");
+
+    if (currencyData != null && currencyCode != null) {
+      writer.die(elem, AT_MOST_ONE_SPECIFIED_CURRENCY);
+    }
+    return currencyData != null ? currencyData : currencyCode;
+  }
+
+  private String consumeFormat(XMLElement elem, UiBinderWriter writer)
+      throws UnableToCompleteException {
+    String format = elem.consumeAttribute("format",
+        writer.getOracle().findType(DateTimeFormat.class.getCanonicalName()));
+    String predefinedFormat = elem.consumeRawAttribute("predefinedFormat");
+    String customFormat = elem.consumeStringAttribute("customFormat");
+
+    if (format != null) {
+      if (predefinedFormat != null || customFormat != null) {
+        writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT);
+      }
+      if (hasCurrency(elem)) {
+        writer.die(elem, NO_CURRENCY_WITH_FORMAT);
+      }
+      return format;
+    }
+    if (predefinedFormat != null) {
+      if (customFormat != null) {
+        writer.die(elem, AT_MOST_ONE_SPECIFIED_FORMAT);
+      }
+      if ("CURRENCY".equals(predefinedFormat)) {
+        String currency = consumeCurrency(elem, writer);
+        return NumberFormat.class.getCanonicalName() + ".getCurrencyFormat("
+            + (currency != null ? currency : "") + ")";
+      }
+      if (hasCurrency(elem)) {
+        writer.die(elem, NO_CURRENCY_WITH_PREDEFINED_FORMAT);
+      }
+      String f = predefinedFormats.get(predefinedFormat);
+      if (f == null) {
+        writer.die(elem, UNKNOWN_PREDEFINED_FORMAT, predefinedFormat);
+      }
+      return f;
+    }
+    if (customFormat != null) {
+      String currency = consumeCurrency(elem, writer);
+      return NumberFormat.class.getCanonicalName() + ".getFormat(" + customFormat
+          + (currency != null ? ", " + currency : "") + ")";
+    }
+    if (hasCurrency(elem)) {
+      writer.die(elem, NO_CURRENCY_WITHOUT_SPECIFIED_FORMAT);
+    }
+    return null;
+  }
+
+  private boolean hasCurrency(XMLElement elem) {
+    return elem.hasAttribute("currencyData")
+        || elem.hasAttribute("currencyCode");
+  }
+
+  private boolean hasNumberFormatConstructor(TypeOracle typeOracle,
+      JClassType type) {
+    JType numberFormatType = typeOracle.findType(NumberFormat.class.getName());
+    return type.findConstructor(new JType[] {numberFormatType}) != null;
+  }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
index bc5c3a3..4297a32 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -264,10 +264,10 @@
     /* Perhaps it is provided via @UiField */
 
     if (ownerField != null) {
-      if (!resourceType.equals(ownerField.getType().getRawType())) {
+      if (!resourceType.getErasedType().equals(ownerField.getType().getRawType()
+          .getErasedType())) {
         writer.die(elem, "Type must match %s", ownerField);
       }
-
       if (ownerField.isProvided()) {
         String initializer;
         if (writer.getDesignTime().isDesignTime()) {
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index e807ac6..a7589b5 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -998,6 +998,8 @@
     addWidgetParser("ListBox");
     addWidgetParser("Grid");
     addWidgetParser("HasAlignment");
+    addWidgetParser("DateLabel");
+    addWidgetParser("NumberLabel");
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/cellview/client/CellBrowser.java b/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
index 1776e45..d92b913 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.animation.client.Animation;
 import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
@@ -300,7 +301,8 @@
         }
 
         SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
-        cell.render(value, getValueKey(value), cellBuilder);
+        Context context = new Context(i, 0, getValueKey(value));
+        cell.render(context, value, cellBuilder);
 
         // Figure out which image to use.
         SafeHtml image;
diff --git a/user/src/com/google/gwt/user/cellview/client/CellList.java b/user/src/com/google/gwt/user/cellview/client/CellList.java
index 9034ae3..2c557d5 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellList.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellList.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.cellview.client;
 
 import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.cell.client.ValueUpdater;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.Scheduler;
@@ -289,18 +290,19 @@
 
   /**
    * Fire an event to the cell.
-   *
+   * 
+   * @param context the {@link Context} of the cell
    * @param event the event that was fired
    * @param parent the parent of the cell
    * @param value the value of the cell
    */
-  protected void fireEventToCell(Event event, Element parent, T value) {
+  protected void fireEventToCell(Context context, Event event, Element parent,
+      T value) {
     Set<String> consumedEvents = cell.getConsumedEvents();
     if (consumedEvents != null && consumedEvents.contains(event.getType())) {
-      Object key = getValueKey(value);
-      boolean cellWasEditing = cell.isEditing(parent, value, key);
-      cell.onBrowserEvent(parent, value, key, event, valueUpdater);
-      cellIsEditing = cell.isEditing(parent, value, key);
+      boolean cellWasEditing = cell.isEditing(context, parent, value);
+      cell.onBrowserEvent(context, parent, value, event, valueUpdater);
+      cellIsEditing = cell.isEditing(context, parent, value);
       if (cellWasEditing && !cellIsEditing) {
         CellBasedWidgetImpl.get().resetFocus(new Scheduler.ScheduledCommand() {
           public void execute() {
@@ -393,8 +395,9 @@
           || KeyboardSelectionPolicy.BOUND_TO_SELECTION == getKeyboardSelectionPolicy();
       Element cellParent = getCellParent(cellTarget);
       T value = getVisibleItem(indexOnPage);
+      Context context = new Context(idx, 0, getValueKey(value));
       CellPreviewEvent<T> previewEvent = CellPreviewEvent.fire(this, event,
-          this, indexOnPage, value, cellIsEditing, isSelectionHandled);
+          this, context, value, cellIsEditing, isSelectionHandled);
       if (isClick && !cellIsEditing && !isSelectionHandled) {
         doSelection(event, value, indexOnPage);
       }
@@ -412,7 +415,7 @@
 
       // Fire the event to the cell if the list has not been refreshed.
       if (!previewEvent.isCanceled()) {
-        fireEventToCell(event, cellParent, value);
+        fireEventToCell(context, event, cellParent, value);
       }
     }
   }
@@ -448,7 +451,8 @@
       }
 
       SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
-      cell.render(value, getValueKey(value), cellBuilder);
+      Context context = new Context(i, 0, getValueKey(value));
+      cell.render(context, value, cellBuilder);
 
       if (i == keyboardSelectedRow) {
         // This is the focused item.
@@ -477,8 +481,8 @@
       Element rowElem = getKeyboardSelectedElement();
       Element cellParent = getCellParent(rowElem);
       T value = getVisibleItem(row);
-      Object key = getValueKey(value);
-      return cell.resetFocus(cellParent, value, key);
+      Context context = new Context(row + getPageStart(), 0, getValueKey(value));
+      return cell.resetFocus(context, cellParent, value);
     }
     return false;
   }
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTable.java b/user/src/com/google/gwt/user/cellview/client/CellTable.java
index 31517bd..0c5e6a6 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTable.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTable.java
@@ -16,18 +16,19 @@
 package com.google.gwt.user.cellview.client;
 
 import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.EventTarget;
 import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.TableCellElement;
 import com.google.gwt.dom.client.TableColElement;
 import com.google.gwt.dom.client.TableElement;
 import com.google.gwt.dom.client.TableRowElement;
 import com.google.gwt.dom.client.TableSectionElement;
-import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.resources.client.ClientBundle;
@@ -292,7 +293,6 @@
 
     @Template("<td class=\"{0}\" align=\"{1}\" valign=\"{2}\">{3}</td>")
     SafeHtml tdBothAlign(String classes, String hAlign, String vAlign,
-
         SafeHtml contents);
 
     @Template("<td class=\"{0}\" align=\"{1}\">{2}</td>")
@@ -939,12 +939,14 @@
     if (section == thead) {
       Header<?> header = headers.get(col);
       if (header != null && cellConsumesEventType(header.getCell(), eventType)) {
-        header.onBrowserEvent(tableCell, event);
+        Context context = new Context(0, col, header.getKey());
+        header.onBrowserEvent(context, tableCell, event);
       }
     } else if (section == tfoot) {
       Header<?> footer = footers.get(col);
       if (footer != null && cellConsumesEventType(footer.getCell(), eventType)) {
-        footer.onBrowserEvent(tableCell, event);
+        Context context = new Context(0, col, footer.getKey());
+        footer.onBrowserEvent(context, tableCell, event);
       }
     } else if (section == tbody) {
       // Update the hover state.
@@ -964,8 +966,7 @@
             style.cellTableHoveredRowCell(), false);
         hoveringRow = null;
       } else if (isClick
-          && ((getPresenter().getKeyboardSelectedRowInView() != row)
-              || (keyboardSelectedColumn != col))) {
+          && ((getPresenter().getKeyboardSelectedRowInView() != row) || (keyboardSelectedColumn != col))) {
         // Move keyboard focus. Since the user clicked, allow focus to go to a
         // non-interactive column.
         boolean isFocusable = CellBasedWidgetImpl.get().isFocusable(target);
@@ -984,14 +985,17 @@
       boolean isSelectionHandled = handlesSelection
           || KeyboardSelectionPolicy.BOUND_TO_SELECTION == getKeyboardSelectionPolicy();
       T value = getVisibleItem(row);
+      Context context = new Context(row + getPageStart(), col,
+          getValueKey(value));
       CellPreviewEvent<T> previewEvent = CellPreviewEvent.fire(this, event,
-          this, row, col, value, cellIsEditing, isSelectionHandled);
+          this, context, value, cellIsEditing, isSelectionHandled);
       if (isClick && !cellIsEditing && !isSelectionHandled) {
         doSelection(event, value, row, col);
       }
 
+      // Pass the event to the cell.
       if (!previewEvent.isCanceled()) {
-        fireEventToCell(event, eventType, tableCell, value, row,
+        fireEventToCell(event, eventType, tableCell, value, context,
             columns.get(col));
       }
     }
@@ -1015,7 +1019,6 @@
     createHeadersAndFooters();
 
     int keyboardSelectedRow = getKeyboardSelectedRow() + getPageStart();
-    ProvidesKey<T> keyProvider = getKeyProvider();
     String evenRowStyle = style.cellTableEvenRow();
     String oddRowStyle = style.cellTableOddRow();
     String cellStyle = style.cellTableCell();
@@ -1075,7 +1078,8 @@
 
         SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
         if (value != null) {
-          column.render(value, keyProvider, cellBuilder);
+          Context context = new Context(i, curColumn, getValueKey(value));
+          column.render(context, value, cellBuilder);
         }
 
         // Build the contents.
@@ -1132,7 +1136,7 @@
     int row = getKeyboardSelectedRow();
     if (isRowWithinBounds(row) && columns.size() > 0) {
       Column<T, ?> column = columns.get(keyboardSelectedColumn);
-      return resetFocusOnCellImpl(row, column);
+      return resetFocusOnCellImpl(row, keyboardSelectedColumn, column);
     }
     return false;
   }
@@ -1218,7 +1222,8 @@
           : style.cellTableFirstColumnHeader());
 
       // Loop through all column headers.
-      for (int curColumn = 1; curColumn < columnCount; curColumn++) {
+      int curColumn;
+      for (curColumn = 1; curColumn < columnCount; curColumn++) {
         Header<?> header = theHeaders.get(curColumn);
 
         if (header != prevHeader) {
@@ -1226,7 +1231,9 @@
           SafeHtmlBuilder headerBuilder = new SafeHtmlBuilder();
           if (prevHeader != null) {
             hasHeader = true;
-            prevHeader.render(headerBuilder);
+            Context context = new Context(0, curColumn - prevColspan,
+                prevHeader.getKey());
+            prevHeader.render(context, headerBuilder);
           }
           sb.append(template.th(prevColspan, classesBuilder.toString(),
               headerBuilder.toSafeHtml()));
@@ -1245,7 +1252,9 @@
       SafeHtmlBuilder headerBuilder = new SafeHtmlBuilder();
       if (prevHeader != null) {
         hasHeader = true;
-        prevHeader.render(headerBuilder);
+        Context context = new Context(0, curColumn - prevColspan,
+            prevHeader.getKey());
+        prevHeader.render(context, headerBuilder);
       }
 
       // The first and last columns could be the same column.
@@ -1348,17 +1357,15 @@
    * Fire an event to the Cell within the specified {@link TableCellElement}.
    */
   private <C> void fireEventToCell(Event event, String eventType,
-      TableCellElement tableCell, T value, int row, Column<T, C> column) {
+      TableCellElement tableCell, T value, Context context,
+      Column<T, C> column) {
     Cell<C> cell = column.getCell();
     if (cellConsumesEventType(cell, eventType)) {
       C cellValue = column.getValue(value);
-      ProvidesKey<T> providesKey = getKeyProvider();
-      Object key = getValueKey(value);
       Element parentElem = getCellParent(tableCell);
-      boolean cellWasEditing = cell.isEditing(parentElem, cellValue, key);
-      column.onBrowserEvent(parentElem, getPageStart() + row, value, event,
-          providesKey);
-      cellIsEditing = cell.isEditing(parentElem, cellValue, key);
+      boolean cellWasEditing = cell.isEditing(context, parentElem, cellValue);
+      column.onBrowserEvent(context, parentElem, value, event);
+      cellIsEditing = cell.isEditing(context, parentElem, cellValue);
       if (cellWasEditing && !cellIsEditing) {
         CellBasedWidgetImpl.get().resetFocus(new Scheduler.ScheduledCommand() {
           public void execute() {
@@ -1438,13 +1445,14 @@
     return consumedEvents != null && consumedEvents.size() > 0;
   }
 
-  private <C> boolean resetFocusOnCellImpl(int row, Column<T, C> column) {
+  private <C> boolean resetFocusOnCellImpl(int row, int col, Column<T, C> column) {
     Element parent = getKeyboardSelectedElement();
     T value = getVisibleItem(row);
     Object key = getValueKey(value);
     C cellValue = column.getValue(value);
     Cell<C> cell = column.getCell();
-    return cell.resetFocus(parent, cellValue, key);
+    Context context = new Context(row + getPageStart(), col, key);
+    return cell.resetFocus(context, parent, cellValue);
   }
 
   /**
@@ -1505,4 +1513,3 @@
     }
   }
 }
-
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
index 5598364..22c0938 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.cellview.client;
 
 import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.dom.client.AnchorElement;
@@ -63,6 +64,7 @@
  *
  * @param <T> the type that this view contains
  */
+// TODO(jlabanca): Convert this to be the type of the child and create lazily.
 class CellTreeNodeView<T> extends UIObject {
 
   interface Template extends SafeHtmlTemplates {
@@ -138,7 +140,8 @@
         // Render the child nodes.
         ProvidesKey<C> keyProvider = nodeInfo.getProvidesKey();
         TreeViewModel model = nodeView.tree.getTreeViewModel();
-        for (C value : values) {
+        for (int i = start; i < end; i++) {
+          C value = values.get(i - start);
           Object key = keyProvider.getKey(value);
           boolean isOpen = openNodes.contains(key);
 
@@ -171,7 +174,8 @@
           }
           // Render cell contents
           SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
-          cell.render(value, key, cellBuilder);
+          Context context = new Context(i, 0, key);
+          cell.render(context, value, cellBuilder);
 
           SafeHtml innerDiv = template.innerDiv(paddingDirection, imageWidth,
               innerClasses.toString(), image, itemValueStyle,
@@ -397,7 +401,6 @@
     }
 
     private final Cell<C> cell;
-
     private final int defaultPageSize;
     private HandlerManager handlerManger = new HandlerManager(this);
     private final NodeInfo<C> nodeInfo;
@@ -999,14 +1002,15 @@
     String eventType = event.getType();
     Element cellParent = getCellParent();
     Object key = getValueKey();
-    boolean cellWasEditing = parentCell.isEditing(cellParent, value, key);
+    Context context = new Context(getIndex(), 0, key);
+    boolean cellWasEditing = parentCell.isEditing(context, cellParent, value);
 
     // Update selection.
     boolean isSelectionHandled = parentCell.handlesSelection()
         || KeyboardSelectionPolicy.BOUND_TO_SELECTION == tree.getKeyboardSelectionPolicy();
     HasData<T> display = (HasData<T>) parentNode.listView;
     CellPreviewEvent<T> previewEvent = CellPreviewEvent.fire(display, event,
-        display, getIndex(), value, cellWasEditing, isSelectionHandled);
+        display, context, value, cellWasEditing, isSelectionHandled);
 
     // Forward the event to the cell.
     if (previewEvent.isCanceled()
@@ -1015,9 +1019,9 @@
     }
     Set<String> consumedEvents = parentCell.getConsumedEvents();
     if (consumedEvents != null && consumedEvents.contains(eventType)) {
-      parentCell.onBrowserEvent(cellParent, value, key, event,
+      parentCell.onBrowserEvent(context, cellParent, value, event,
           parentNodeInfo.getValueUpdater());
-      tree.cellIsEditing = parentCell.isEditing(cellParent, value, key);
+      tree.cellIsEditing = parentCell.isEditing(context, cellParent, value);
       if (cellWasEditing && !tree.cellIsEditing) {
         CellBasedWidgetImpl.get().resetFocus(new Scheduler.ScheduledCommand() {
           public void execute() {
@@ -1198,13 +1202,14 @@
 
   /**
    * Reset focus on this node.
-   *
+   * 
    * @return true of the cell takes focus, false if not
    */
   boolean resetFocusOnCell() {
     if (parentNodeInfo != null) {
+      Context context = new Context(getIndex(), 0, getValueKey());
       Cell<T> cell = parentNodeInfo.getCell();
-      return cell.resetFocus(getCellParent(), value, getValueKey());
+      return cell.resetFocus(context, getCellParent(), value);
     }
     return false;
   }
diff --git a/user/src/com/google/gwt/user/cellview/client/Column.java b/user/src/com/google/gwt/user/cellview/client/Column.java
index 0ad4215..f74f5da 100644
--- a/user/src/com/google/gwt/user/cellview/client/Column.java
+++ b/user/src/com/google/gwt/user/cellview/client/Column.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.cellview.client;
 
 import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.cell.client.FieldUpdater;
 import com.google.gwt.cell.client.HasCell;
 import com.google.gwt.cell.client.ValueUpdater;
@@ -23,7 +24,6 @@
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
 import com.google.gwt.user.client.ui.HasAlignment;
-import com.google.gwt.view.client.ProvidesKey;
 
 /**
  * A representation of a column in a table. The column may maintain view data
@@ -93,35 +93,32 @@
   /**
    * Handle a browser event that took place within the column.
    * 
+   * @param context the cell context
    * @param elem the parent Element
-   * @param index the current row index of the object
    * @param object the base object to be updated
    * @param event the native browser event
-   * @param keyProvider an instance of ProvidesKey<T>, or null if the record
-   *          object should act as its own key.
    */
-  public void onBrowserEvent(Element elem, final int index, final T object,
-      NativeEvent event, ProvidesKey<T> keyProvider) {
-    Object key = getKey(object, keyProvider);
+  public void onBrowserEvent(Context context, Element elem, final T object,
+      NativeEvent event) {
+    final int index = context.getIndex();
     ValueUpdater<C> valueUpdater = (fieldUpdater == null) ? null
         : new ValueUpdater<C>() {
           public void update(C value) {
             fieldUpdater.update(index, object, value);
           }
         };
-    cell.onBrowserEvent(elem, getValue(object), key, event, valueUpdater);
+    cell.onBrowserEvent(context, elem, getValue(object), event, valueUpdater);
   }
 
   /**
    * Render the object into the cell.
    * 
+   * @param context the cell context
    * @param object the object to render
-   * @param keyProvider the {@link ProvidesKey} for the object
    * @param sb the buffer to render into
    */
-  public void render(T object, ProvidesKey<T> keyProvider, SafeHtmlBuilder sb) {
-    Object key = getKey(object, keyProvider);
-    cell.render(getValue(object), key, sb);
+  public void render(Context context, T object, SafeHtmlBuilder sb) {
+    cell.render(context, getValue(object), sb);
   }
 
   /**
@@ -156,16 +153,4 @@
   public void setVerticalAlignment(VerticalAlignmentConstant align) {
     this.vAlign = align;
   }
-
-  /**
-   * Get the view key for the object given the {@link ProvidesKey}. If the
-   * {@link ProvidesKey} is null, the object is used as the key.
-   * 
-   * @param object the row object
-   * @param keyProvider the {@link ProvidesKey}
-   * @return the key for the object
-   */
-  private Object getKey(T object, ProvidesKey<T> keyProvider) {
-    return keyProvider == null ? object : keyProvider.getKey(object);
-  }
 }
diff --git a/user/src/com/google/gwt/user/cellview/client/Header.java b/user/src/com/google/gwt/user/cellview/client/Header.java
index 3739fc3..c38ad35 100644
--- a/user/src/com/google/gwt/user/cellview/client/Header.java
+++ b/user/src/com/google/gwt/user/cellview/client/Header.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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
@@ -16,6 +16,7 @@
 package com.google.gwt.user.cellview.client;
 
 import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.cell.client.ValueUpdater;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -23,7 +24,7 @@
 
 /**
  * A table column header or footer.
- *
+ * 
  * @param <H> the {@link Cell} type
  */
 public abstract class Header<H> {
@@ -43,7 +44,7 @@
 
   /**
    * Return the {@link Cell} responsible for rendering items in the header.
-   *
+   * 
    * @return the header Cell
    */
   public Cell<H> getCell() {
@@ -51,30 +52,41 @@
   }
 
   /**
+   * Get the key for the header value. By default, the key is the same as the
+   * value. Override this method to return a custom key.
+   * 
+   * @return the key associated with the value
+   */
+  public Object getKey() {
+    return getValue();
+  }
+
+  /**
    * Return the header value.
-   *
+   * 
    * @return the header value
    */
   public abstract H getValue();
 
   /**
    * Handle a browser event that took place within the header.
-   *
+   * 
+   * @param context the context of the header
    * @param elem the parent Element
    * @param event the native browser event
    */
-  public void onBrowserEvent(Element elem, NativeEvent event) {
-    H value = getValue();
-    cell.onBrowserEvent(elem, value, getKey(), event, updater);
+  public void onBrowserEvent(Context context, Element elem, NativeEvent event) {
+    cell.onBrowserEvent(context, elem, getValue(), event, updater);
   }
 
   /**
    * Render the header.
    * 
+   * @param context the context of the header
    * @param sb a {@link SafeHtmlBuilder} to render into
    */
-  public void render(SafeHtmlBuilder sb) {
-    cell.render(getValue(), getKey(), sb);
+  public void render(Context context, SafeHtmlBuilder sb) {
+    cell.render(context, getValue(), sb);
   }
 
   /**
@@ -85,14 +97,4 @@
   public void setUpdater(ValueUpdater<H> updater) {
     this.updater = updater;
   }
-
-  /**
-   * Get the key for the header value. By default, the key is the same as the
-   * value. Override this method to return a custom key.
-   *
-   * @return the key associated with the value
-   */
-  protected Object getKey() {
-    return getValue();
-  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/Anchor.java b/user/src/com/google/gwt/user/client/ui/Anchor.java
index bda6fce..35de8ef 100644
--- a/user/src/com/google/gwt/user/client/ui/Anchor.java
+++ b/user/src/com/google/gwt/user/client/ui/Anchor.java
@@ -20,7 +20,8 @@
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection;
-import com.google.gwt.safehtml.client.HasSafeHtml;
+import com.google.gwt.i18n.shared.DirectionEstimator;
+import com.google.gwt.i18n.shared.HasDirectionEstimator;
 import com.google.gwt.safehtml.shared.SafeHtml;
 
 /**
@@ -30,6 +31,14 @@
  * If you want use this anchor only for changing history states, use
  * {@link Hyperlink} instead.
  * </p>
+ *
+ * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
  * 
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
@@ -39,7 +48,11 @@
  * @see Hyperlink
  */
 public class Anchor extends FocusWidget implements HasHorizontalAlignment,
-    HasName, HasHTML, HasWordWrap, HasDirection, HasSafeHtml {
+    HasName, HasHTML, HasWordWrap, HasDirection,
+    HasDirectionEstimator, HasDirectionalSafeHtml {
+
+  public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR =
+      DirectionalTextHelper.DEFAULT_DIRECTION_ESTIMATOR;
 
   /**
    * Creates an Anchor widget that wraps an existing &lt;a&gt; element.
@@ -63,6 +76,8 @@
     return anchor;
   }
 
+  private final DirectionalTextHelper directionalTextHelper;
+
   private HorizontalAlignmentConstant horzAlign;
 
   /**
@@ -71,6 +86,45 @@
   public Anchor() {
     setElement(Document.get().createAnchorElement());
     setStyleName("gwt-Anchor");
+    directionalTextHelper = new DirectionalTextHelper(getAnchorElement(),
+        /* is inline */ true);
+  }
+
+  /**
+   * Creates an anchor for scripting.
+   *
+   * @param html the anchor's html
+   */
+  public Anchor(SafeHtml html) {
+    this(html.asString(), true);
+  }
+
+  /**
+   * Creates an anchor for scripting.
+   *
+   * The anchor's href is set to <code>javascript : ;</code>, based on the
+   * expectation that listeners will be added to the anchor.
+   *
+   * @param html the anchor's html
+   * @param dir the html's direction
+   */
+  public Anchor(SafeHtml html, Direction dir) {
+    this(html.asString(), true, dir, "javascript:;");
+  }
+  
+  /**
+   * Creates an anchor for scripting.
+   *
+   * The anchor's href is set to <code>javascript : ;</code>, based on the
+   * expectation that listeners will be added to the anchor.
+   *
+   * @param html the anchor's html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   */
+  public Anchor(SafeHtml html, DirectionEstimator directionEstimator) {
+    this(html.asString(), true, directionEstimator, "javascript:;");
   }
 
   /**
@@ -88,10 +142,14 @@
   /**
    * Creates an anchor for scripting.
    *
-   * @param html the anchor's text
+   * The anchor's href is set to <code>javascript : ;</code>, based on the
+   * expectation that listeners will be added to the anchor.
+   *
+   * @param text the anchor's text
+   * @param dir the text's direction
    */
-  public Anchor(SafeHtml html) {
-    this(html.asString(), true);
+  public Anchor(String text, Direction dir) {
+    this(text, dir, "javascript:;");
   }
 
   /**
@@ -101,6 +159,21 @@
    * expectation that listeners will be added to the anchor.
    *
    * @param text the anchor's text
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   */
+  public Anchor(String text, DirectionEstimator directionEstimator) {
+    this(text, directionEstimator, "javascript:;");
+  }
+
+  /**
+   * Creates an anchor for scripting.
+   * 
+   * The anchor's href is set to <code>javascript:;</code>, based on the
+   * expectation that listeners will be added to the anchor.
+   * 
+   * @param text the anchor's text
    * @param asHtml <code>true</code> to treat the specified text as html
    */
   public Anchor(String text, boolean asHtml) {
@@ -108,7 +181,7 @@
   }
 
   /**
-   * Creates an anchor with its text and href (target URL) specified.
+   * Creates an anchor with its html and href (target URL) specified.
    *
    * @param html the anchor's html
    * @param href the url to which it will link
@@ -118,6 +191,66 @@
   }
 
   /**
+   *  Creates an anchor with its html and href (target URL) specified.
+   *
+   * @param html the anchor's html
+   * @param dir the html's direction
+   * @param href the url to which it will link
+   */
+  public Anchor(SafeHtml html, Direction dir, String href) {
+    this(html.asString(), true, dir, href);
+  }
+  
+  /**
+   *  Creates an anchor with its html and href (target URL) specified.
+   *
+   * @param html the anchor's html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param href the url to which it will link
+   */
+  public Anchor(SafeHtml html, DirectionEstimator directionEstimator,
+      String href) {
+    this(html.asString(), true, directionEstimator, href);
+  }
+
+  /**
+   * Creates an anchor with its text and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param href the url to which it will link
+   */
+  public Anchor(String text, String href) {
+    this(text, false, href);
+  }
+
+  /**
+   * Creates an anchor with its text and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param dir the text's direction
+   * @param href the url to which it will link
+   */
+  public Anchor(String text, Direction dir, String href) {
+    this(text, false, dir, href);
+  }
+
+  /**
+   * Creates an anchor with its text and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param href the url to which it will link
+   */
+  public Anchor(String text, DirectionEstimator directionEstimator,
+      String href) {
+    this(text, false, directionEstimator, href);
+  }
+
+  /**
    * Creates an anchor with its text and href (target URL) specified.
    *
    * @param text the anchor's text
@@ -126,11 +259,7 @@
    */
   public Anchor(String text, boolean asHTML, String href) {
     this();
-    if (asHTML) {
-      setHTML(text);
-    } else {
-      setText(text);
-    }
+    directionalTextHelper.setTextOrHtml(text, asHTML);
     setHref(href);
   }
 
@@ -149,6 +278,18 @@
   }
 
   /**
+   * Creates a source anchor with a frame target.
+   * 
+   * @param text the anchor's text
+   * @param href the url to which it will link
+   * @param target the target frame (e.g. "_blank" to open the link in a new
+   *          window)
+   */
+  public Anchor(String text, String href, String target) {
+    this(text, false, href, target);
+  }
+
+  /**
    * Creates a source anchor (link to URI).
    *
    * That is, an anchor with an href attribute specifying the destination URI.
@@ -165,30 +306,6 @@
   }
 
   /**
-   * Creates an anchor with its text and href (target URL) specified.
-   * 
-   * @param text the anchor's text
-   * @param href the url to which it will link
-   */
-  public Anchor(String text, String href) {
-    this();
-    setText(text);
-    setHref(href);
-  }
-
-  /**
-   * Creates a source anchor with a frame target.
-   * 
-   * @param text the anchor's text
-   * @param href the url to which it will link
-   * @param target the target frame (e.g. "_blank" to open the link in a new
-   *          window)
-   */
-  public Anchor(String text, String href, String target) {
-    this(text, false, href, target);
-  }
-
-  /**
    * This constructor may be used by subclasses to explicitly use an existing
    * element. This element must be an &lt;a&gt; element.
    * 
@@ -197,12 +314,50 @@
   protected Anchor(Element element) {
     AnchorElement.as(element);
     setElement(element);
+    directionalTextHelper = new DirectionalTextHelper(getAnchorElement(),
+        /* is inline */ true);
+  }
+
+  /**
+   * Creates an anchor with its text, direction and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param dir the text's direction
+   * @param href the url to which it will link
+   */
+  private Anchor(String text, boolean asHTML, Direction dir, String href) {
+    this();
+    directionalTextHelper.setTextOrHtml(text, dir, asHTML);
+    setHref(href);
+  }
+  
+  /**
+   * Creates an anchor with its text, direction and href (target URL) specified.
+   * 
+   * @param text the anchor's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param href the url to which it will link
+   */
+  private Anchor(String text, boolean asHTML,
+      DirectionEstimator directionEstimator, String href) {
+    this();
+    directionalTextHelper.setDirectionEstimator(directionEstimator);
+    directionalTextHelper.setTextOrHtml(text, asHTML);
+    setHref(href);
   }
 
   public Direction getDirection() {
     return BidiUtils.getDirectionOnElement(getElement());
   }
 
+  public DirectionEstimator getDirectionEstimator() {
+    return directionalTextHelper.getDirectionEstimator();
+  }
+
   public HorizontalAlignmentConstant getHorizontalAlignment() {
     return horzAlign;
   }
@@ -240,7 +395,11 @@
   }
 
   public String getText() {
-    return getElement().getInnerText();
+    return directionalTextHelper.getTextOrHtml(false);
+  }
+
+  public Direction getTextDirection() {
+    return directionalTextHelper.getTextDirection();
   }
 
   public boolean getWordWrap() {
@@ -252,8 +411,35 @@
     getAnchorElement().setAccessKey(Character.toString(key));
   }
 
+  /**
+   * @deprecated Use {@link #setDirectionEstimator} and / or pass explicit
+   * direction to {@link #setText}, {@link #setHTML} instead
+   */
+  @Deprecated
   public void setDirection(Direction direction) {
-    BidiUtils.setDirectionOnElement(getElement(), direction);
+    directionalTextHelper.setDirection(direction);
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * See note at {@link #setDirectionEstimator(DirectionEstimator)}.
+   */
+  public void setDirectionEstimator(boolean enabled) {
+    directionalTextHelper.setDirectionEstimator(enabled);
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * Note: DirectionEstimator should be set before the widget has any content;
+   * it's highly recommended to set it using a constructor. Reason: if the
+   * widget already has non-empty content, this will update its direction
+   * according to the new estimator's result. This may cause flicker, and thus
+   * should be avoided.
+   */
+  public void setDirectionEstimator(DirectionEstimator directionEstimator) {
+    directionalTextHelper.setDirectionEstimator(directionEstimator);
   }
 
   @Override
@@ -284,7 +470,11 @@
   }
 
   public void setHTML(String html) {
-    getElement().setInnerHTML(html);
+    directionalTextHelper.setTextOrHtml(html, true);
+  }
+
+  public void setHTML(SafeHtml html, Direction dir) {
+    directionalTextHelper.setTextOrHtml(html.asString(), dir, true);
   }
 
   public void setName(String name) {
@@ -307,7 +497,11 @@
   }
 
   public void setText(String text) {
-    getElement().setInnerText(text);
+    directionalTextHelper.setTextOrHtml(text, false);
+  }
+
+  public void setText(String text, Direction dir) {
+    directionalTextHelper.setTextOrHtml(text, dir, false);
   }
 
   public void setWordWrap(boolean wrap) {
diff --git a/user/src/com/google/gwt/user/client/ui/CheckBox.java b/user/src/com/google/gwt/user/client/ui/CheckBox.java
index 75635ee..3549e0d 100644
--- a/user/src/com/google/gwt/user/client/ui/CheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -59,6 +59,7 @@
     HasWordWrap, IsEditor<LeafValueEditor<Boolean>> {
   InputElement inputElem;
   LabelElement labelElem;
+  private LeafValueEditor<Boolean> editor;
   private boolean valueChangeHandlerInitialized;
 
   /**
@@ -134,7 +135,10 @@
   }
 
   public LeafValueEditor<Boolean> asEditor() {
-    return TakesValueEditor.of(this);
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
   }
 
   /**
@@ -172,7 +176,7 @@
   /**
    * Determines whether this check box is currently checked.
    * <p>
-   * Note that this <em>is not</em> return the value property of the checkbox
+   * Note that this <em>does not</em> return the value property of the checkbox
    * input element wrapped by this widget. For access to that property, see
    * {@link #getFormValue()}
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/DateLabel.java b/user/src/com/google/gwt/user/client/ui/DateLabel.java
new file mode 100644
index 0000000..0c925a8
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DateLabel.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010 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.user.client.ui;
+
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.TimeZone;
+import com.google.gwt.text.client.DateTimeFormatRenderer;
+
+import java.util.Date;
+
+/**
+ * Extends {@link ValueLabel} for convenience when dealing with dates and
+ * {@link DateTimeFormat}, especially in
+ * {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates. (Note
+ * that this class does not accept renderers. To do so use {@link ValueLabel}
+ * directly.)
+ * 
+ * <h3>Use in UiBinder Templates</h3>
+ * In {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates, both the format and time zone can be configured.
+ * <p>
+ * The format can be given with one of these attributes:
+ * <dl>
+ * <dt>format</dt><dd>a reference to a {@link DateTimeFormat} instance.</dd>
+ * <dt>predefinedFormat</dt><dd>a {@link DateTimeFormat.PredefinedFormat}.</dd>
+ * <dt>customFormat</dt><dd>a date time pattern that can be passed to {@link DateTimeFormat#getFormat(String)}.</dd>
+ * </dl>
+ * <p>
+ * The time zone can be specified with either of these attributes:
+ * <dl>
+ * <dt>timezone</dt><dd>a reference to a {@link TimeZone} instance.</dd>
+ * <dt>timezoneOffset</dt><dd>the time zone offset in minutes.</dd>
+ * </dl>
+ */
+public class DateLabel extends ValueLabel<Date> {
+
+  public DateLabel() {
+    super(new DateTimeFormatRenderer());
+  }
+
+  public DateLabel(DateTimeFormat format) {
+    super(new DateTimeFormatRenderer(format));
+  }
+
+  public DateLabel(DateTimeFormat format, TimeZone timeZone) {
+    super(new DateTimeFormatRenderer(format, timeZone));
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/DirectionalTextHelper.java b/user/src/com/google/gwt/user/client/ui/DirectionalTextHelper.java
new file mode 100644
index 0000000..68d3fbd
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DirectionalTextHelper.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2010 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.user.client.ui;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.i18n.shared.BidiFormatter;
+import com.google.gwt.i18n.shared.DirectionEstimator;
+import com.google.gwt.i18n.shared.HasDirectionEstimator;
+import com.google.gwt.i18n.shared.WordCountDirectionEstimator;
+
+/**
+ * A helper class for displaying bidi (i.e. potentially opposite-direction) text 
+ * or HTML in an element.
+ * Note: this class assumes that callers perform all their text/html and
+ * direction manipulations through it alone.
+ */
+public class DirectionalTextHelper implements HasDirectionEstimator {
+
+  /**
+   * A default direction estimator instance.
+   */
+  public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR =
+      WordCountDirectionEstimator.get();
+
+  /**
+   * The DirectionEstimator object.
+   */
+  private DirectionEstimator directionEstimator;
+
+  /**
+   * The target element.
+   */
+  private final Element element;
+
+  /**
+   * The initial direction of the element.
+   */
+  private Direction initialElementDir;
+
+  /**
+   * Whether direction was explicitly set on the last {@code setTextOrHtml}
+   * call. If so, {@link #setDirectionEstimator} will refrain from modifying the
+   * direction until {@link #setTextOrHtml} is called without specifying an
+   * explicit direction.
+   */
+  private boolean isDirectionExplicitlySet;
+
+  /**
+   * Whether the element is inline (e.g. a &lt;span&gt; element, but not a block
+   * element like &lt;div&gt;).
+   * This is needed because direction is handled differently for inline elements
+   * and for non-inline elements.
+   */
+  private final boolean isElementInline;
+
+  /**
+   * Whether the element contains a nested &lt;span&gt; element used to
+   * indicate the content's direction.
+   * <p>
+   * The element itself is used for this purpose when it is a block element
+   * (i.e. !isElementInline), but doing so on an inline element often results in
+   * garbling what follows it. Thus, when the element is inline, a nested
+   * &lt;span&gt; must be used to carry the content's direction, with an LRM or
+   * RLM character afterwards to prevent the garbling.
+   */
+  private boolean isSpanWrapped;
+
+  /**
+   * The direction of the element's content.
+   * Note: this may not match the direction attribute of the element itself.
+   * See
+   * {@link #setTextOrHtml(String, com.google.gwt.i18n.client.HasDirection.Direction, boolean) setTextOrHtml(String, Direction, boolean)}
+   * for details.
+   */
+  private Direction textDir;
+
+  /**
+   * @param element The widget's element holding text.
+   * @param isElementInline Whether the element is an inline element.
+   */
+  public DirectionalTextHelper(Element element, boolean isElementInline) {
+    this.element = element;
+    this.isElementInline = isElementInline;
+    isSpanWrapped = false;
+    this.initialElementDir = BidiUtils.getDirectionOnElement(element);
+    textDir = initialElementDir;
+    // setDirectionEstimator shouldn't refresh appearance of initial empty text.
+    isDirectionExplicitlySet = true;
+  }
+
+  public DirectionEstimator getDirectionEstimator() {
+    return directionEstimator;
+  }
+
+  public Direction getTextDirection() {
+    return textDir;
+  }
+
+  /**
+   * Get the inner text or html of the element, taking the inner span wrap into
+   * consideration, if needed.
+   * 
+   * @param isHtml true to get the inner html, false to get the inner text
+   * @return the text or html
+   */
+  public String getTextOrHtml(boolean isHtml) {
+    Element elem = isSpanWrapped ? element.getFirstChildElement() : element;
+    return isHtml ? elem.getInnerHTML() : elem.getInnerText();
+  }
+
+  /**
+   * Provides implementation for HasDirection's method setDirection (normally
+   * deprecated), dealing with backwards compatibility issues.
+   * @deprecated
+   */
+  @Deprecated
+  public void setDirection(Direction direction) {
+    BidiUtils.setDirectionOnElement(element, direction);
+    initialElementDir = direction;
+
+    /* 
+     * For backwards compatibility, assure there's no span wrap, and update the
+     * content direction.
+     */
+    setInnerTextOrHtml(getTextOrHtml(true), true);
+    isSpanWrapped = false;
+    textDir = initialElementDir;
+    isDirectionExplicitlySet = true;
+  }
+
+  /**
+   * See note at
+   * {@link #setDirectionEstimator(com.google.gwt.i18n.shared.DirectionEstimator)}.
+   */
+  public void setDirectionEstimator(boolean enabled) {
+    setDirectionEstimator(enabled ? DEFAULT_DIRECTION_ESTIMATOR : null);
+  }
+
+  /**
+   * Note: if the element already has non-empty content, this will update
+   * its direction according to the new estimator's result. This may cause
+   * flicker, and thus should be avoided; DirectionEstimator should be set
+   * before the element has any content.
+   */
+  public void setDirectionEstimator(DirectionEstimator directionEstimator) {
+    this.directionEstimator = directionEstimator;
+    /* 
+     * Refresh appearance unless direction was explicitly set on last
+     * setTextOrHtml call.
+     */
+    if (!isDirectionExplicitlySet) {
+      setTextOrHtml(getTextOrHtml(true), true);
+    }
+  }
+
+  /**
+   * Sets the element's content to the given value (either plain text or HTML).
+   * If direction estimation is off, the direction is verified to match the
+   * element's initial direction. Otherwise, the direction is affected as
+   * described at
+   * {@link #setTextOrHtml(String, com.google.gwt.i18n.client.HasDirection.Direction, boolean) setTextOrHtml(String, Direction, boolean)}.
+   *
+   * @param content the element's new content
+   * @param isHtml whether the content is HTML
+   */
+  public void setTextOrHtml(String content, boolean isHtml) {
+    if (directionEstimator == null) {
+      isSpanWrapped = false;
+      setInnerTextOrHtml(content, isHtml);
+
+      /*
+       * Preserves the initial direction of the element. This is different from
+       * passing the direction parameter explicitly as DEFAULT, which forces the
+       * element to inherit the direction from its parent.
+       */
+      if (textDir != initialElementDir) {
+        textDir = initialElementDir;
+        BidiUtils.setDirectionOnElement(element, initialElementDir);
+      }
+    } else {
+      setTextOrHtml(content, directionEstimator.estimateDirection(content,
+          isHtml), isHtml);
+    }
+    isDirectionExplicitlySet = false;
+  }
+
+  /**
+   * Sets the element's content to the given value (either plain text or HTML),
+   * applying the given direction.
+   * <p>
+   * Implementation details:
+   * <ul>
+   * <li> If the element is a block element, sets its dir attribute according
+   * to the given direction.
+   * <li> Otherwise (i.e. the element is inline), the direction is set using a
+   * nested &lt;span dir=...&gt; element which holds the content of the element.
+   * This nested span may be followed by a zero-width Unicode direction
+   * character (LRM or RLM). This manipulation is necessary to prevent garbling
+   * in case the direction of the element is opposite to the direction of its
+   * context. See {@link com.google.gwt.i18n.shared.BidiFormatter} for more
+   * details.
+   * </ul>
+   *
+   * @param content the element's new content
+   * @param dir the content's direction
+   * @param isHtml whether the content is HTML
+   */
+  public void setTextOrHtml(String content, Direction dir, boolean isHtml) {
+    textDir = dir;
+    // Set the text and the direction.
+    if (isElementInline) {
+      isSpanWrapped = true;
+      element.setInnerHTML(BidiFormatter.getInstanceForCurrentLocale(
+          true /* alwaysSpan */).spanWrapWithKnownDir(dir, content, isHtml));
+    } else {
+      isSpanWrapped = false;
+      BidiUtils.setDirectionOnElement(element, dir);
+      setInnerTextOrHtml(content, isHtml);
+    }
+    isDirectionExplicitlySet = true;
+  }
+
+  private void setInnerTextOrHtml(String content, boolean isHtml) {
+    if (isHtml) {
+      element.setInnerHTML(content);
+    } else {
+      element.setInnerText(content);
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/HTML.java b/user/src/com/google/gwt/user/client/ui/HTML.java
index 6fe54bf..e7e123d 100644
--- a/user/src/com/google/gwt/user/client/ui/HTML.java
+++ b/user/src/com/google/gwt/user/client/ui/HTML.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.shared.DirectionEstimator;
 import com.google.gwt.safehtml.shared.SafeHtml;
 
 /**
@@ -32,6 +33,14 @@
  * used properly.
  * </p>
  *
+ * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-HTML { }</li>
@@ -86,18 +95,8 @@
   }
 
   /**
-   * Creates an HTML widget with the specified HTML contents.
-   *
-   * @param html the new widget's HTML contents
-   */
-  public HTML(String html) {
-    this();
-    setHTML(html);
-  }
-
-  /**
-   * Creates an HTML widget with the specified contents and with the
-   * specified direction.
+   * Creates an HTML widget with the specified contents and with the specified
+   * direction.
    *
    * @param html the new widget's SafeHtml contents
    * @param dir the content's direction. Note: {@code Direction.DEFAULT} means
@@ -108,6 +107,31 @@
   }
 
   /**
+   * Creates an HTML widget with the specified HTML contents and specifies a
+   * direction estimator.
+   *
+   * @param html the new widget's SafeHtml contents
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link Label#DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   */
+  public HTML(SafeHtml html, DirectionEstimator directionEstimator) {
+    this();
+    setDirectionEstimator(directionEstimator);
+    setHTML(html);
+  }
+
+  /**
+   * Creates an HTML widget with the specified HTML contents.
+   *
+   * @param html the new widget's HTML contents
+   */
+  public HTML(String html) {
+    this();
+    setHTML(html);
+  }
+
+  /**
    * Creates an HTML widget with the specified HTML contents and with the
    * specified direction.
    *
@@ -145,7 +169,7 @@
   }
 
   public String getHTML() {
-    return getTextOrHtml(true);
+    return directionalTextHelper.getTextOrHtml(true);
   }
 
   /**
@@ -156,21 +180,23 @@
    * @param html the new widget's HTML content
    */
   public void setHTML(String html) {
-    setTextOrHtml(html, true);
+    directionalTextHelper.setTextOrHtml(html, true);
+    updateHorizontalAlignment();
   }
 
   /**
    * Sets the label's content to the given HTML, applying the given direction.
    * See
-   * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction)
-   * setText(String, Direction)} for details on potential effects on alignment.
+   * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction) setText(String, Direction)}
+   * for details on potential effects on alignment.
    * 
    * @param html the new widget's HTML content
    * @param dir the content's direction. Note: {@code Direction.DEFAULT} means
    *          direction should be inherited from the widget's parent element.
    */
   public void setHTML(String html, Direction dir) {
-    setTextOrHtml(html, dir, true);
+    directionalTextHelper.setTextOrHtml(html, dir, true);
+    updateHorizontalAlignment();
   }
 
   /**
@@ -188,4 +214,8 @@
   public void setHTML(SafeHtml html, Direction dir) {
     setHTML(html.asString(), dir);
   }
+
+  protected String getTextOrHtml(boolean isHtml) {
+    return directionalTextHelper.getTextOrHtml(isHtml);
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/Hidden.java b/user/src/com/google/gwt/user/client/ui/Hidden.java
index fcd978b..d9f1466 100644
--- a/user/src/com/google/gwt/user/client/ui/Hidden.java
+++ b/user/src/com/google/gwt/user/client/ui/Hidden.java
@@ -18,11 +18,15 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.editor.client.IsEditor;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.editor.client.adapters.TakesValueEditor;
+import com.google.gwt.user.client.TakesValue;
 
 /**
  * Represents a hidden field in an HTML form.
  */
-public class Hidden extends Widget implements HasName {
+public class Hidden extends Widget implements HasName, TakesValue<String>, IsEditor<LeafValueEditor<String>> {
 
   /**
    * Creates a Hidden widget that wraps an existing &lt;input type='hidden'&gt;
@@ -47,6 +51,8 @@
     return hidden;
   }
 
+  private LeafValueEditor<String> editor;
+
   /**
    * Constructor for <code>Hidden</code>.
    */
@@ -87,6 +93,13 @@
     setElement(element);
   }
 
+  public LeafValueEditor<String> asEditor() {
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
+  }
+
   /**
    * Gets the default value of the hidden field.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/Hyperlink.java b/user/src/com/google/gwt/user/client/ui/Hyperlink.java
index 9d8f2e0..08e25f8 100644
--- a/user/src/com/google/gwt/user/client/ui/Hyperlink.java
+++ b/user/src/com/google/gwt/user/client/ui/Hyperlink.java
@@ -20,7 +20,9 @@
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.HasClickHandlers;
 import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.safehtml.client.HasSafeHtml;
+import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.i18n.shared.DirectionEstimator;
+import com.google.gwt.i18n.shared.HasDirectionEstimator;
 import com.google.gwt.safehtml.shared.SafeHtml;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
@@ -46,6 +48,14 @@
  * </p>
  * 
  * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
+ *
+ * <p>
  * <img class='gallery' src='doc-files/Hyperlink.png'/>
  * </p>
  * 
@@ -62,10 +72,14 @@
  */
 @SuppressWarnings("deprecation")
 public class Hyperlink extends Widget implements HasHTML, SourcesClickEvents,
-  HasClickHandlers, HasSafeHtml {
+    HasClickHandlers, HasDirectionEstimator, HasDirectionalSafeHtml {
+
+  public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR =
+      DirectionalTextHelper.DEFAULT_DIRECTION_ESTIMATOR;
 
   private static HyperlinkImpl impl = GWT.create(HyperlinkImpl.class);
-  
+
+  protected final DirectionalTextHelper directionalTextHelper;
   private final Element anchorElem = DOM.createAnchor();
   private String targetHistoryToken;
 
@@ -88,21 +102,30 @@
   }
 
   /**
-   * Creates a hyperlink with its text and target history token specified.
+   * Creates a hyperlink with its html and target history token specified.
    *
-   * @param text the hyperlink's text
-   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param html the hyperlink's safe html
+   * @param dir the html's direction
    * @param targetHistoryToken the history token to which it will link
    * @see #setTargetHistoryToken
    */
-  public Hyperlink(String text, boolean asHTML, String targetHistoryToken) {
-    this();
-    if (asHTML) {
-      setHTML(text);
-    } else {
-      setText(text);
-    }
-    setTargetHistoryToken(targetHistoryToken);
+  public Hyperlink(SafeHtml html, Direction dir, String targetHistoryToken) {
+    this(html.asString(), true, dir, targetHistoryToken);
+  }
+  
+  /**
+   * Creates a hyperlink with its html and target history token specified.
+   *
+   * @param html the hyperlink's safe html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  public Hyperlink(SafeHtml html, DirectionEstimator directionEstimator,
+      String targetHistoryToken) {
+    this(html.asString(), true, directionEstimator, targetHistoryToken);
   }
 
   /**
@@ -114,8 +137,49 @@
    *          history processing)
    */
   public Hyperlink(String text, String targetHistoryToken) {
+    this(text, false, targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   * 
+   * @param text the hyperlink's text
+   * @param dir the text's direction
+   * @param targetHistoryToken the history token to which it will link, which
+   *          may not be null (use {@link Anchor} instead if you don't need
+   *          history processing)
+   */
+  public Hyperlink(String text, Direction dir, String targetHistoryToken) {
+    this(text, false, dir, targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   * 
+   * @param text the hyperlink's text
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param targetHistoryToken the history token to which it will link, which
+   *          may not be null (use {@link Anchor} instead if you don't need
+   *          history processing)
+   */
+  public Hyperlink(String text, DirectionEstimator directionEstimator,
+      String targetHistoryToken) {
+    this(text, false, directionEstimator, targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   *
+   * @param text the hyperlink's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  public Hyperlink(String text, boolean asHTML, String targetHistoryToken) {
     this();
-    setText(text);
+    directionalTextHelper.setTextOrHtml(text, asHTML);
     setTargetHistoryToken(targetHistoryToken);
   }
   
@@ -129,6 +193,43 @@
 
     sinkEvents(Event.ONCLICK);
     setStyleName("gwt-Hyperlink");
+    directionalTextHelper = new DirectionalTextHelper(anchorElem,
+        /* is inline */ true);
+  }
+
+  /**
+   * Creates a hyperlink with its text target history token specified.
+   *
+   * @param text the hyperlink's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param dir the text's direction
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  private Hyperlink(String text, boolean asHTML, Direction dir,
+      String targetHistoryToken) {
+    this();
+    directionalTextHelper.setTextOrHtml(text, dir, asHTML);
+    setTargetHistoryToken(targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   *
+   * @param text the hyperlink's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  private Hyperlink(String text, boolean asHTML,
+      DirectionEstimator directionEstimator, String targetHistoryToken) {
+    this();
+    directionalTextHelper.setDirectionEstimator(directionEstimator);
+    directionalTextHelper.setTextOrHtml(text, asHTML);
+    setTargetHistoryToken(targetHistoryToken);
   }
 
   /**
@@ -151,8 +252,12 @@
     ListenerWrapper.WrappedClickListener.add(this, listener);
   }
 
+  public DirectionEstimator getDirectionEstimator() {
+    return directionalTextHelper.getDirectionEstimator();
+  }
+
   public String getHTML() {
-    return DOM.getInnerHTML(anchorElem);
+    return directionalTextHelper.getTextOrHtml(true);
   }
 
   /**
@@ -166,7 +271,11 @@
   }
 
   public String getText() {
-    return DOM.getInnerText(anchorElem);
+    return directionalTextHelper.getTextOrHtml(false);
+  }
+
+  public Direction getTextDirection() {
+    return directionalTextHelper.getTextDirection();
   }
 
   @Override
@@ -187,14 +296,40 @@
     ListenerWrapper.WrappedClickListener.remove(this, listener);
   }
 
-  public void setHTML(String html) {
-    DOM.setInnerHTML(anchorElem, html);
+  /**
+   * {@inheritDoc}
+   * <p>
+   * See note at {@link #setDirectionEstimator(DirectionEstimator)}.
+   */
+  public void setDirectionEstimator(boolean enabled) {
+    directionalTextHelper.setDirectionEstimator(enabled);
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * Note: DirectionEstimator should be set before the widget has any content;
+   * it's highly recommended to set it using a constructor. Reason: if the
+   * widget already has non-empty content, this will update its direction
+   * according to the new estimator's result. This may cause flicker, and thus
+   * should be avoided.
+   */
+  public void setDirectionEstimator(DirectionEstimator directionEstimator) {
+    directionalTextHelper.setDirectionEstimator(directionEstimator);
   }
 
   public void setHTML(SafeHtml html) {
     setHTML(html.asString());
   }
 
+  public void setHTML(String html) {
+    directionalTextHelper.setTextOrHtml(html, true);
+  }
+
+  public void setHTML(SafeHtml html, Direction dir) {
+    directionalTextHelper.setTextOrHtml(html.asString(), dir, true);
+  }
+
   /**
    * Sets the history token referenced by this hyperlink. This is the history
    * token that will be passed to {@link History#newItem} when this link is
@@ -211,7 +346,11 @@
   }
 
   public void setText(String text) {
-    DOM.setInnerText(anchorElem, text);
+    directionalTextHelper.setTextOrHtml(text, false);
+  }
+
+  public void setText(String text, Direction dir) {
+    directionalTextHelper.setTextOrHtml(text, dir, false);
   }
 
   /**
@@ -228,4 +367,3 @@
     ensureDebugId(getElement(), baseID, "wrapper");
   }
 }
-
diff --git a/user/src/com/google/gwt/user/client/ui/InlineHTML.java b/user/src/com/google/gwt/user/client/ui/InlineHTML.java
index a1a0bc1..d12e526 100644
--- a/user/src/com/google/gwt/user/client/ui/InlineHTML.java
+++ b/user/src/com/google/gwt/user/client/ui/InlineHTML.java
@@ -17,6 +17,8 @@
 
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
+
+import com.google.gwt.i18n.shared.DirectionEstimator;
 import com.google.gwt.safehtml.shared.SafeHtml;
 
 /**
@@ -32,6 +34,14 @@
  * used properly.
  * </p>
  *
+ * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-InlineHTML { }</li>
@@ -80,16 +90,6 @@
   }
 
   /**
-   * Creates an HTML widget with the specified HTML contents.
-   *
-   * @param html the new widget's HTML contents
-   */
-  public InlineHTML(String html) {
-    this();
-    setHTML(html);
-  }
-
-  /**
    * Creates an HTML widget with the specified contents and with the
    * specified direction.
    *
@@ -102,6 +102,31 @@
   }
 
   /**
+   * Creates an HTML widget with the specified HTML contents and with a default
+   * direction estimator.
+   *
+   * @param html the new widget's SafeHtml contents
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link Label#DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   */
+  public InlineHTML(SafeHtml html, DirectionEstimator directionEstimator) {
+    this();
+    setDirectionEstimator(directionEstimator);
+    setHTML(html);
+  }
+
+  /**
+   * Creates an HTML widget with the specified HTML contents.
+   *
+   * @param html the new widget's HTML contents
+   */
+  public InlineHTML(String html) {
+    this();
+    setHTML(html);
+  }
+
+  /**
    * Creates an HTML widget with the specified HTML contents and with the
    * specified direction.
    *
diff --git a/user/src/com/google/gwt/user/client/ui/InlineHyperlink.java b/user/src/com/google/gwt/user/client/ui/InlineHyperlink.java
index 663d6f9..43b20be 100644
--- a/user/src/com/google/gwt/user/client/ui/InlineHyperlink.java
+++ b/user/src/com/google/gwt/user/client/ui/InlineHyperlink.java
@@ -16,6 +16,8 @@
 
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.i18n.shared.DirectionEstimator;
 import com.google.gwt.safehtml.shared.SafeHtml;
 
 /**
@@ -24,6 +26,14 @@
  * {@link com.google.gwt.user.client.ui.Hyperlink}, save that it lays out
  * as an inline element, not block.
  * 
+ * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-InlineHyperlink { }</li>
@@ -52,22 +62,31 @@
   }
 
   /**
-   * Creates a hyperlink with its text and target history token specified.
-   *
-   * @param text the hyperlink's text
-   * @param asHTML <code>true</code> to treat the specified text as html
+   * Creates a hyperlink with its html and target history token specified.
+   * 
+   * @param html the hyperlink's html
+   * @param dir the html's direction
    * @param targetHistoryToken the history token to which it will link
    * @see #setTargetHistoryToken
    */
-  public InlineHyperlink(String text, boolean asHTML, String targetHistoryToken) {
-    this();
-
-    if (asHTML) {
-      setHTML(text);
-    } else {
-      setText(text);
-    }
-    setTargetHistoryToken(targetHistoryToken);
+  public InlineHyperlink(SafeHtml html, Direction dir,
+      String targetHistoryToken) {
+    this(html.asString(), true, dir, targetHistoryToken);
+  }
+  
+  /**
+   * Creates a hyperlink with its html and target history token specified.
+   *
+   * @param html the hyperlink's html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link Hyperlink#DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  public InlineHyperlink(SafeHtml html, DirectionEstimator directionEstimator,
+      String targetHistoryToken) {
+    this(html.asString(), true, directionEstimator, targetHistoryToken);
   }
 
   /**
@@ -79,4 +98,80 @@
   public InlineHyperlink(String text, String targetHistoryToken) {
     this(text, false, targetHistoryToken);
   }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   * 
+   * @param text the hyperlink's text
+   * @param dir the text's direction
+   * @param targetHistoryToken the history token to which it will link
+   */
+  public InlineHyperlink(String text, Direction dir,
+      String targetHistoryToken) {
+    this(text, false, dir, targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   * 
+   * @param text the hyperlink's text
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link Hyperlink#DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param targetHistoryToken the history token to which it will link
+   */
+  public InlineHyperlink(String text, DirectionEstimator directionEstimator,
+      String targetHistoryToken) {
+    this(text, false, directionEstimator, targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   *
+   * @param text the hyperlink's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  public InlineHyperlink(String text, boolean asHTML,
+      String targetHistoryToken) {
+    this();
+    directionalTextHelper.setTextOrHtml(text, asHTML);
+    setTargetHistoryToken(targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   *
+   * @param text the hyperlink's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param dir the text's direction
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  private InlineHyperlink(String text, boolean asHTML, Direction dir,
+      String targetHistoryToken) {
+    this();
+    directionalTextHelper.setTextOrHtml(text, dir, asHTML);
+    setTargetHistoryToken(targetHistoryToken);
+  }
+
+  /**
+   * Creates a hyperlink with its text and target history token specified.
+   *
+   * @param text the hyperlink's text
+   * @param asHTML <code>true</code> to treat the specified text as html
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link Hyperlink#DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   * @param targetHistoryToken the history token to which it will link
+   * @see #setTargetHistoryToken
+   */
+  private InlineHyperlink(String text, boolean asHTML,
+      DirectionEstimator directionEstimator, String targetHistoryToken) {
+    this();
+    directionalTextHelper.setDirectionEstimator(directionEstimator);
+    directionalTextHelper.setTextOrHtml(text, asHTML);
+    setTargetHistoryToken(targetHistoryToken);
+  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/InlineLabel.java b/user/src/com/google/gwt/user/client/ui/InlineLabel.java
index 75bafab..a6b9efc 100644
--- a/user/src/com/google/gwt/user/client/ui/InlineLabel.java
+++ b/user/src/com/google/gwt/user/client/ui/InlineLabel.java
@@ -18,12 +18,22 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 
+import com.google.gwt.i18n.shared.DirectionEstimator;
+
 /**
  * A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
  *
  * This widget uses a &lt;span&gt; element, causing it to be displayed with
  * inline layout.
  *
+ * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-InlineLabel { }</li>
@@ -85,6 +95,20 @@
   }
 
   /**
+   * Creates a label with the specified text and a default direction estimator.
+   *
+   * @param text the new label's text
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link Label#DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   */
+  public InlineLabel(String text, DirectionEstimator directionEstimator) {
+    this();
+    setDirectionEstimator(directionEstimator);
+    setText(text);
+  }
+  
+  /**
    * This constructor may be used by subclasses to explicitly use an existing
    * element. This element must be either a &lt;div&gt; &lt;span&gt; element.
    *
diff --git a/user/src/com/google/gwt/user/client/ui/Label.java b/user/src/com/google/gwt/user/client/ui/Label.java
index 9811f76..8d94dde 100644
--- a/user/src/com/google/gwt/user/client/ui/Label.java
+++ b/user/src/com/google/gwt/user/client/ui/Label.java
@@ -42,11 +42,7 @@
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection;
-import com.google.gwt.i18n.shared.BidiFormatter;
 import com.google.gwt.i18n.shared.DirectionEstimator;
-import com.google.gwt.i18n.shared.HasDirectionEstimator;
-import com.google.gwt.i18n.shared.WordCountDirectionEstimator;
-import com.google.gwt.safehtml.shared.SafeHtml;
 
 /**
  * A widget that contains arbitrary text, <i>not</i> interpreted as HTML.
@@ -54,6 +50,14 @@
  * This widget uses a &lt;div&gt; element, causing it to be displayed with block
  * layout.
  *
+ * <p>
+ * <h3>Built-in Bidi Text Support</h3>
+ * This widget is capable of automatically adjusting its direction according to
+ * its content. This feature is controlled by {@link #setDirectionEstimator} or
+ * passing a DirectionEstimator parameter to the constructor, and is off by
+ * default.
+ * </p>
+ *
  * <h3>CSS Style Rules</h3>
  * <ul class='css'>
  * <li>.gwt-Label { }</li>
@@ -65,10 +69,12 @@
  * </p>
  */
 @SuppressWarnings("deprecation")
-public class Label extends Widget implements HasDirectionalText, HasWordWrap,
+public class Label extends LabelBase<String> implements HasDirectionalText,
     HasDirection, HasClickHandlers, HasDoubleClickHandlers, SourcesClickEvents,
-    SourcesMouseEvents, HasAllMouseHandlers, HasDirectionEstimator,
-    HasAutoHorizontalAlignment, IsEditor<LeafValueEditor<String>> {
+    SourcesMouseEvents, HasAllMouseHandlers, IsEditor<LeafValueEditor<String>> {
+
+  public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR =
+      DirectionalTextHelper.DEFAULT_DIRECTION_ESTIMATOR;
 
   /**
    * Creates a Label widget that wraps an existing &lt;div&gt; or &lt;span&gt;
@@ -93,69 +99,14 @@
     return label;
   }
 
-  /**
-   * The widget's auto horizontal alignment policy.
-   * @see HasAutoHorizontalAlignment
-   */
-  private AutoHorizontalAlignmentConstant autoHorizontalAlignment;
-
-  /**
-   * The direction of the widget's content.
-   * Note: this may not match the direction of the widget's top DOM element
-   * ({@code getElement()}).
-   * See {@link #setTextOrHtml(String, Direction, boolean)} for details.
-   */
-  private Direction textDir;
-
-  /**
-   * The widget's DirectionEstimator object.
-   */
-  private DirectionEstimator directionEstimator;
-
-  /**
-   * The widget's horizontal alignment.
-   */
-  private HorizontalAlignmentConstant horzAlign;
-
-  /**
-   * The initial direction of the widget's element.
-   */
-  private Direction initialElementDir;
-
-  /**
-   * Whether the widget is inline (a &lt;span&gt; element).
-   * This is needed because direction is handled differently for inline elements
-   * and for non-inline elements.
-   * <p>
-   * In case Label supports types of elements other than span and div, this
-   * should get true for any element that is inline by default. Another approach
-   * could be calculating the element's display property, but this may have some
-   * overhead, and is problematic when the element is yet unattached.
-   */
-  private boolean isElementInline;
-
-  /**
-   * Whether the widget contains a nested &lt;span&gt; element used to
-   * indicate the content's direction.
-   * <p>
-   * The widget's top element is used for this purpose when it is a &lt;div&gt;,
-   * but doing so on an inline element often results in garbling what follows
-   * it. Thus, when the widget's top element is a &lt;span&gt;, a nested
-   * &lt;span&gt; must be used to carry the content's direction, with an LRM or
-   * RLM character afterwards to prevent the garbling.
-   */
-  private boolean isSpanWrapped;
+  private LeafValueEditor<String> editor;
 
   /**
    * Creates an empty label.
    */
   public Label() {
-    setElement(Document.get().createDivElement());
+    super(false);
     setStyleName("gwt-Label");
-    isElementInline = false;
-    isSpanWrapped = false;
-    textDir = Direction.DEFAULT;
-    initialElementDir = Direction.DEFAULT;
   }
 
   /**
@@ -173,7 +124,7 @@
    *
    * @param text the new label's text
    * @param dir the text's direction. Note that {@code DEFAULT} means direction
-   *        should be inherited from the widget's parent element.
+   *          should be inherited from the widget's parent element.
    */
   public Label(String text, Direction dir) {
     this();
@@ -181,6 +132,20 @@
   }
 
   /**
+   * Creates a label with the specified text and a default direction estimator.
+   *
+   * @param text the new label's text
+   * @param directionEstimator A DirectionEstimator object used for automatic
+   *          direction adjustment. For convenience,
+   *          {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
+   */
+  public Label(String text, DirectionEstimator directionEstimator) {
+    this();
+    setDirectionEstimator(directionEstimator);
+    setText(text);
+  }
+
+  /**
    * Creates a label with the specified text.
    *
    * @param text the new label's text
@@ -198,13 +163,7 @@
    * @param element the element to be used
    */
   protected Label(Element element) {
-    setElement(element);
-    String tagName = element.getTagName();
-    isElementInline = tagName.equalsIgnoreCase("span");
-    assert isElementInline || tagName.equalsIgnoreCase("div");
-    isSpanWrapped = false;
-    initialElementDir = BidiUtils.getDirectionOnElement(element);
-    textDir = initialElementDir;
+    super(element);
   }
 
   public HandlerRegistration addClickHandler(ClickHandler handler) {
@@ -266,14 +225,10 @@
   }
 
   public LeafValueEditor<String> asEditor() {
-    return HasTextEditor.of(this);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public AutoHorizontalAlignmentConstant getAutoHorizontalAlignment() {
-    return autoHorizontalAlignment;
+    if (editor == null) {
+      editor = HasTextEditor.of(this);
+    }
+    return editor;
   }
 
   /**
@@ -285,27 +240,12 @@
     return BidiUtils.getDirectionOnElement(getElement());
   }
 
-  public DirectionEstimator getDirectionEstimator() {
-    return directionEstimator;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public HorizontalAlignmentConstant getHorizontalAlignment() {
-    return horzAlign;
-  }
-
   public String getText() {
-    return getTextOrHtml(false);
+    return directionalTextHelper.getTextOrHtml(false);
   }
 
   public Direction getTextDirection() {
-    return textDir;
-  }
-
-  public boolean getWordWrap() {
-    return !getElement().getStyle().getProperty("whiteSpace").equals("nowrap");
+    return directionalTextHelper.getTextDirection();
   }
 
   /**
@@ -336,82 +276,29 @@
   }
 
   /**
-   * {@inheritDoc}
-   */
-  public void setAutoHorizontalAlignment(AutoHorizontalAlignmentConstant
-      autoAlignment) {
-    autoHorizontalAlignment = autoAlignment;
-    updateHorizontalAlignment();
-  }
-
-  /**
    * Sets the widget element's direction.
    * @deprecated Use {@link #setDirectionEstimator} and / or pass explicit
    * direction to {@link #setText} instead
    */
   @Deprecated
   public void setDirection(Direction direction) {
-    BidiUtils.setDirectionOnElement(getElement(), direction);
-    initialElementDir = direction;
-
-    // For backwards compatibility, assure there's no span wrap, and update the
-    // content direction.
-    setInnerTextOrHtml(getTextOrHtml(true), true);
-    isSpanWrapped = false;
-    textDir = initialElementDir;
+    directionalTextHelper.setDirection(direction);
     updateHorizontalAlignment();
   }
 
   /**
-   * {@inheritDoc}
-   * <p>
-   * See note at {@link #setDirectionEstimator(DirectionEstimator)}.
-   */
-  public void setDirectionEstimator(boolean enabled) {
-    setDirectionEstimator(enabled ? WordCountDirectionEstimator.get() : null);
-  }
-
-  /**
-   * {@inheritDoc}
-   * <p>
-   * Note: if the widget already has non-empty content, this will update
-   * its direction according to the new estimator's result. This may cause
-   * flicker, and thus should be avoided; DirectionEstimator should be set
-   * before the widget has any content.
-   */
-  public void setDirectionEstimator(DirectionEstimator directionEstimator) {
-    this.directionEstimator = directionEstimator;
-    // Refresh appearance
-    setTextOrHtml(getTextOrHtml(true), true);
-  }
-
-  /**
-   * {@inheritDoc}
-   *
-   * <p> Note: A subsequent call to {@link #setAutoHorizontalAlignment} may
-   * override the horizontal alignment set by this method.
-   * <p> Note: For {@code null}, the horizontal alignment is cleared, allowing
-   * it to be determined by the standard HTML mechanisms such as inheritance and
-   * CSS rules.
-   * @see #setAutoHorizontalAlignment
-   */
-  public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
-    setAutoHorizontalAlignment(align);
-  }
-
-  /**
    * Sets the label's content to the given text.
    * <p>
    * Doesn't change the widget's direction or horizontal alignment if {@code
    * directionEstimator} is null. Otherwise, the widget's direction is set using
    * the estimator, and its alignment may therefore change as described in
-   * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction)
-   * setText(String, Direction)}.
-   * 
+   * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction) setText(String, Direction)}.
+   *
    * @param text the widget's new text
    */
   public void setText(String text) {
-    setTextOrHtml(text, false);
+    directionalTextHelper.setTextOrHtml(text, false);
+    updateHorizontalAlignment();
   }
 
   /**
@@ -435,141 +322,7 @@
    *        direction should be inherited from the widget's parent element.
    */
   public void setText(String text, Direction dir) {
-    setTextOrHtml(text, dir, false);
-  }
-
-  public void setWordWrap(boolean wrap) {
-    getElement().getStyle().setProperty("whiteSpace",
-        wrap ? "normal" : "nowrap");
-  }
-
-  protected String getTextOrHtml(boolean isHtml) {
-    Element element = isSpanWrapped ? getElement().getFirstChildElement()
-        : getElement();
-    return isHtml ? element.getInnerHTML() : element.getInnerText();
-  }
-
-  /**
-   * Sets the label's content to the given safe html. See
-   * {@link #setText(String)} for details on potential effects on direction and
-   * alignment.
-   *
-   * @param html the widget's new safe html
-   */
-  protected void setHTML(SafeHtml html) {
-    setTextOrHtml(html.asString(), true);
-  }
-
-  /**
-   * Sets the label's content to the given safe html. See
-   * {@link #setText(String)} for details on potential effects on direction and
-   * alignment.
-   *
-   * @param html the widget's new safe html
-   * @param dir the content's direction
-   */
-  protected void setHTML(SafeHtml html, Direction dir) {
-    setTextOrHtml(html.asString(), dir, true);
-  }
-
-  /**
-   * Sets the label's content to the given value (either plain text or HTML).
-   * See {@link #setText(String)} for details on potential effects on direction
-   * and alignment.
-   *
-   * @param content the widget's new content
-   * @param isHtml whether the content is HTML
-   */
-  protected void setTextOrHtml(String content, boolean isHtml) {
-    if (directionEstimator == null) {
-      isSpanWrapped = false;
-      setInnerTextOrHtml(content, isHtml);
-
-      // Preserves the initial direction of the widget. This is different from
-      // passing the direction parameter explicitly as DEFAULT, which forces the
-      // widget to inherit the direction from its parent.
-      if (textDir != initialElementDir) {
-        textDir = initialElementDir;
-        BidiUtils.setDirectionOnElement(getElement(), initialElementDir);
-        updateHorizontalAlignment();
-      }
-    } else {
-      setTextOrHtml(content, directionEstimator.estimateDirection(content,
-          isHtml), isHtml);
-    }
-  }
-
-  /**
-   * Sets the label's content to the given value (either plain text or HTML),
-   * applying the given direction. See
-   * {@link #setText(String, com.google.gwt.i18n.client.HasDirection.Direction)
-   * setText(String, Direction)} for details on potential effects on alignment.
-   * <p>
-   * Implementation details:
-   * <ul>
-   * <li>If the widget's element is a &lt;div&gt;, sets its dir attribute
-   * according to the given direction.
-   * <li>Otherwise (i.e. the widget's element is a &lt;span&gt;), the direction
-   * is set using a nested &lt;span dir=...&gt; element which holds the content
-   * of the widget. This nested span may be followed by a zero-width Unicode
-   * direction character (LRM or RLM). This manipulation is necessary to prevent
-   * garbling in case the direction of the widget is opposite to the direction
-   * of its context. See {@link com.google.gwt.i18n.shared.BidiFormatter} for
-   * more details.
-   * </ul>
-   * 
-   * @param content the widget's new content
-   * @param dir the content's direction
-   * @param isHtml whether the content is HTML
-   */
-  protected void setTextOrHtml(String content, Direction dir, boolean isHtml) {
-    textDir = dir;
-
-    // Set the text and the direction.
-    if (isElementInline) {
-      isSpanWrapped = true;
-      getElement().setInnerHTML(BidiFormatter.getInstanceForCurrentLocale(
-          true /* alwaysSpan */).spanWrapWithKnownDir(dir, content, isHtml));
-    } else {
-      isSpanWrapped = false;
-      BidiUtils.setDirectionOnElement(getElement(), dir);
-      setInnerTextOrHtml(content, isHtml);
-    }
-
-    // Update the horizontal alignment if needed.
+    directionalTextHelper.setTextOrHtml(text, dir, false);
     updateHorizontalAlignment();
   }
-
-  private void setInnerTextOrHtml(String content, boolean isHtml) {
-    if (isHtml) {
-      getElement().setInnerHTML(content);
-    } else {
-      getElement().setInnerText(content);
-    }
-  }
-
-  /**
-   * Sets the horizontal alignment of the widget according to the current
-   * AutoHorizontalAlignment setting.
-   */
-  private void updateHorizontalAlignment() {
-    HorizontalAlignmentConstant align;
-    if (autoHorizontalAlignment == null) {
-      align = null;
-    } else if (autoHorizontalAlignment instanceof HorizontalAlignmentConstant) {
-      align = (HorizontalAlignmentConstant) autoHorizontalAlignment;
-    } else {
-      /* autoHorizontalAlignment is a truly automatic policy, i.e. either
-      ALIGN_CONTENT_START or ALIGN_CONTENT_END */
-      align = autoHorizontalAlignment == ALIGN_CONTENT_START ?
-          HorizontalAlignmentConstant.startOf(textDir) :
-          HorizontalAlignmentConstant.endOf(textDir);
-    }
-
-    if (align != horzAlign) {
-      horzAlign = align;
-      getElement().getStyle().setProperty("textAlign", horzAlign == null ? ""
-          : horzAlign.getTextAlignString());
-    }
-  }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/LabelBase.java b/user/src/com/google/gwt/user/client/ui/LabelBase.java
new file mode 100644
index 0000000..4f15d3f
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/LabelBase.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2010 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.user.client.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.shared.DirectionEstimator;
+import com.google.gwt.i18n.shared.HasDirectionEstimator;
+
+/**
+ * Abstract base class for all text display widgets.
+ * 
+ * <h3>Use in UiBinder Templates</h3>
+ * 
+ * @param <T> the value type
+ */
+public class LabelBase<T> extends Widget implements HasWordWrap,
+    HasDirectionEstimator, HasAutoHorizontalAlignment {
+
+  /**
+   * The widget's DirectionalTextHelper object.
+   */
+  final DirectionalTextHelper directionalTextHelper;
+
+  /**
+   * The widget's auto horizontal alignment policy.
+   * 
+   * @see HasAutoHorizontalAlignment
+   */
+  private AutoHorizontalAlignmentConstant autoHorizontalAlignment;
+
+  /**
+   * The widget's horizontal alignment.
+   */
+  private HorizontalAlignmentConstant horzAlign;
+
+  protected LabelBase(boolean inline) {
+    this(inline ? Document.get().createSpanElement()
+        : Document.get().createDivElement(), inline);
+  }
+
+  protected LabelBase(Element element) {
+    this(element, "span".equalsIgnoreCase(element.getTagName()));
+  }
+
+  private LabelBase(Element element, boolean isElementInline) {
+    assert (isElementInline ? "span" : "div").equalsIgnoreCase(element.getTagName());
+    setElement(element);
+    directionalTextHelper = new DirectionalTextHelper(getElement(),
+        isElementInline);
+  }
+
+  public AutoHorizontalAlignmentConstant getAutoHorizontalAlignment() {
+    return autoHorizontalAlignment;
+  }
+
+  public DirectionEstimator getDirectionEstimator() {
+    return directionalTextHelper.getDirectionEstimator();
+  }
+
+  public HorizontalAlignmentConstant getHorizontalAlignment() {
+    return horzAlign;
+  }
+
+  public boolean getWordWrap() {
+    return !getElement().getStyle().getProperty("whiteSpace").equals("nowrap");
+  }
+
+  public void setAutoHorizontalAlignment(
+      AutoHorizontalAlignmentConstant autoAlignment) {
+    autoHorizontalAlignment = autoAlignment;
+    updateHorizontalAlignment();
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * See note at {@link #setDirectionEstimator(DirectionEstimator)}.
+   */
+  public void setDirectionEstimator(boolean enabled) {
+    directionalTextHelper.setDirectionEstimator(enabled);
+    updateHorizontalAlignment();
+  }
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * Note: DirectionEstimator should be set before the widget has any content;
+   * it's highly recommended to set it using a constructor. Reason: if the
+   * widget already has non-empty content, this will update its direction
+   * according to the new estimator's result. This may cause flicker, and thus
+   * should be avoided.
+   */
+  public void setDirectionEstimator(DirectionEstimator directionEstimator) {
+    directionalTextHelper.setDirectionEstimator(directionEstimator);
+    updateHorizontalAlignment();
+  }
+
+  /**
+   * {@inheritDoc}
+   * 
+   * <p>
+   * Note: A subsequent call to {@link #setAutoHorizontalAlignment} may override
+   * the horizontal alignment set by this method.
+   * <p>
+   * Note: For {@code null}, the horizontal alignment is cleared, allowing it to
+   * be determined by the standard HTML mechanisms such as inheritance and CSS
+   * rules.
+   * 
+   * @see #setAutoHorizontalAlignment
+   */
+  public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
+    setAutoHorizontalAlignment(align);
+  }
+
+  public void setWordWrap(boolean wrap) {
+    getElement().getStyle().setProperty("whiteSpace",
+        wrap ? "normal" : "nowrap");
+  }
+
+  /**
+   * Sets the horizontal alignment of the widget according to the current
+   * AutoHorizontalAlignment setting. Should be invoked whenever the horizontal
+   * alignment may be affected, i.e. on every modification of the content or its
+   * direction.
+   */
+  protected void updateHorizontalAlignment() {
+    HorizontalAlignmentConstant align;
+    if (autoHorizontalAlignment == null) {
+      align = null;
+    } else if (autoHorizontalAlignment instanceof HorizontalAlignmentConstant) {
+      align = (HorizontalAlignmentConstant) autoHorizontalAlignment;
+    } else {
+      /*
+       * autoHorizontalAlignment is a truly automatic policy, i.e. either
+       * ALIGN_CONTENT_START or ALIGN_CONTENT_END
+       */
+      align = autoHorizontalAlignment == ALIGN_CONTENT_START
+          ? HorizontalAlignmentConstant.startOf(directionalTextHelper.getTextDirection())
+          : HorizontalAlignmentConstant.endOf(directionalTextHelper.getTextDirection());
+    }
+
+    if (align != horzAlign) {
+      horzAlign = align;
+      getElement().getStyle().setProperty("textAlign",
+          horzAlign == null ? "" : horzAlign.getTextAlignString());
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/NumberLabel.java b/user/src/com/google/gwt/user/client/ui/NumberLabel.java
new file mode 100644
index 0000000..ad398ca
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/NumberLabel.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010 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.user.client.ui;
+
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.text.client.NumberFormatRenderer;
+
+/**
+ * Extends {@link ValueLabel} for convenience when dealing with numbers and
+ * {@link NumberFormat}, especially in
+ * {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates. (Note
+ * that this class does not accept renderers. To do so use {@link ValueLabel}
+ * directly.)
+ * 
+ * <h3>Use in UiBinder Templates</h3> In
+ * {@link com.google.gwt.uibinder.client.UiBinder UiBinder} templates, the
+ * {@link NumberFormat} can be specified with one of these attributes:
+ * <dl>
+ * <dt>format</dt>
+ * <dd>a reference to a {@link NumberFormat} instance.</dd>
+ * <dt>predefinedFormat</dt>
+ * <dd>a predefined format (see below for the list of acceptable values).</dd>
+ * <dt>customFormat</dt>
+ * <dd>a number format pattern that can be passed to
+ * {@link NumberFormat#getFormat(String)}. See below for a way of specifying a
+ * currency code.</dd>
+ * </dl>
+ * The valid values for the {@code predefinedFormat} attributes are:
+ * <dl>
+ * <dt>DECIMAL</dt>
+ * <dd>the standard decimal format for the current locale, as given by
+ * {@link NumberFormat#getDecimalFormat()}.</dd>
+ * <dt>CURRENCY</dt>
+ * <dd>the standard currency format for the current locale, as given by
+ * {@link NumberFormat#getCurrencyFormat()}. See below for a way of specifying a
+ * currency code.</dd>
+ * <dt>PERCENT</dt>
+ * <dd>the standard percent format for the current locale, as given by
+ * {@link NumberFormat#getPercentFormat()}.</dd>
+ * <dt>SCIENTIFIC</dt>
+ * <dd>the standard scientific format for the current locale, as given by
+ * {@link NumberFormat#getScientificFormat()}.</dd>
+ * </dl>
+ * When using {@code predefinedFormat="CURRENCY"} or a {@code customFormat}, you
+ * can specify a currency code using either of the following attributes:
+ * <dl>
+ * <dt>currencyData</dt>
+ * <dd>a reference to a {@link com.google.gwt.i18n.client.CurrencyData
+ * CurrencyData} instance.</dd>
+ * <dt>currencyCode</dt>
+ * <dd>an ISO4217 currency code.</dd>
+ * </dl>
+ * 
+ * @param <T> The exact type of number
+ */
+public class NumberLabel<T extends Number> extends ValueLabel<T> {
+
+  public NumberLabel() {
+    super(new NumberFormatRenderer());
+  }
+
+  public NumberLabel(NumberFormat format) {
+    super(new NumberFormatRenderer(format));
+  }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
index 4bc465c..4975554 100644
--- a/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/SimpleCheckBox.java
@@ -18,6 +18,10 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.editor.client.IsEditor;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.editor.client.adapters.TakesValueEditor;
+import com.google.gwt.user.client.TakesValue;
 
 /**
  * A simple checkbox widget, with no label.
@@ -28,7 +32,8 @@
  * <li>.gwt-SimpleCheckBox-disabled { Applied when checkbox is disabled }</li>
  * </ul>
  */
-public class SimpleCheckBox extends FocusWidget implements HasName {
+public class SimpleCheckBox extends FocusWidget implements HasName,
+    TakesValue<Boolean>, IsEditor<LeafValueEditor<Boolean>> {
 
   /**
    * Creates a SimpleCheckBox widget that wraps an existing &lt;input
@@ -53,6 +58,8 @@
     return checkBox;
   }
 
+  private LeafValueEditor<Boolean> editor;
+
   /**
    * Creates a new simple checkbox.
    */
@@ -79,28 +86,66 @@
     }
   }
 
+  public LeafValueEditor<Boolean> asEditor() {
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
+  }
+
+  /**
+   * Returns the value property of the input element that backs this widget.
+   * This is the value that will be associated with the check box name and
+   * submitted to the server if a {@link FormPanel} that holds it is submitted
+   * and the box is checked.
+   * <p>
+   * Don't confuse this with {@link #getValue}, which returns true or false if
+   * the widget is checked.
+   */
+  public String getFormValue() {
+    return getInputElement().getValue();
+  }
+
   public String getName() {
     return getInputElement().getName();
   }
 
   /**
    * Determines whether this check box is currently checked.
+   * <p>
+   * Note that this <em>does not</em> return the value property of the checkbox
+   * input element wrapped by this widget. For access to that property, see
+   * {@link #getFormValue()}
    * 
-   * @return <code>true</code> if the check box is checked
+   * @return <code>true</code> if the check box is checked, false otherwise.
+   *         Will not return null
    */
-  public boolean isChecked() {
+  public Boolean getValue() {
     String propName = isAttached() ? "checked" : "defaultChecked";
     return getInputElement().getPropertyBoolean(propName);
   }
 
   /**
+   * Determines whether this check box is currently checked.
+   * 
+   * @return <code>true</code> if the check box is checked
+   * @deprecated Use {@link #getValue} instead
+   */
+  @Deprecated
+  public boolean isChecked() {
+    // Funny comparison b/c getValue could in theory return null
+    return getValue() == true;
+  }
+
+  /**
    * Checks or unchecks this check box.
    * 
    * @param checked <code>true</code> to check the check box
+   * @deprecated Use {@link #setValue(Boolean)} instead
    */
+  @Deprecated
   public void setChecked(boolean checked) {
-    getInputElement().setChecked(checked);
-    getInputElement().setDefaultChecked(checked);
+    setValue(checked);
   }
 
   @Override
@@ -113,18 +158,50 @@
     }
   }
 
+  /**
+   * Set the value property on the input element that backs this widget. This is
+   * the value that will be associated with the check box's name and submitted
+   * to the server if a {@link FormPanel} that holds it is submitted and the box
+   * is checked.
+   * <p>
+   * Don't confuse this with {@link #setValue}, which actually checks and
+   * unchecks the box.
+   * 
+   * @param value
+   */
+  public void setFormValue(String value) {
+    getInputElement().setAttribute("value", value);
+  }
+
   public void setName(String name) {
     getInputElement().setName(name);
   }
 
   /**
+   * Checks or unchecks the check box.
+   * <p>
+   * Note that this <em>does not</em> set the value property of the checkbox
+   * input element wrapped by this widget. For access to that property, see
+   * {@link #setFormValue(String)}
+   * 
+   * @param value true to check, false to uncheck; null value implies false
+   */
+  public void setValue(Boolean value) {
+    if (value == null) {
+      value = Boolean.FALSE;
+    }
+
+    getInputElement().setChecked(value);
+    getInputElement().setDefaultChecked(value);
+  }
+
+  /**
    * This method is called when a widget is detached from the browser's
-   * document. Overridden because of IE bug that throws away checked state and
-   * in order to clear the event listener off of the <code>inputElem</code>.
+   * document. Overridden because of IE bug that throws away checked state.
    */
   @Override
   protected void onUnload() {
-    setChecked(isChecked());
+    setValue(getValue());
   }
 
   private InputElement getInputElement() {
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestBox.java b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
index a291aec..919a5f5 100644
--- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java
+++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -657,6 +657,7 @@
   private boolean selectsFirstItem = true;
   private SuggestOracle oracle;
   private String currentText;
+  private LeafValueEditor<String> editor;
   private final SuggestionDisplay display;
   private final TextBoxBase box;
   private final Callback callback = new Callback() {
@@ -814,7 +815,10 @@
    * Returns a {@link TakesValueEditor} backed by the DateBox.
    */
   public LeafValueEditor<String> asEditor() {
-    return TakesValueEditor.of(this);
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/client/ui/TabPanel.java b/user/src/com/google/gwt/user/client/ui/TabPanel.java
index 1253cdf..fc609e9 100644
--- a/user/src/com/google/gwt/user/client/ui/TabPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/TabPanel.java
@@ -59,17 +59,12 @@
  * {@example com.google.gwt.examples.TabPanelExample}
  * </p>
  *
- * @deprecated Use {@link TabLayoutPanel} instead, but understand that it is
- *             not a drop in replacement for this class. It requires standards
- *             mode, and is most easily used under a {@link RootLayoutPanel} (as
- *             opposed to a {@link RootPanel}
- *
  * @see TabLayoutPanel
  */
 
 // Cannot do anything about tab panel implementing TabListener until next
 // release
-@Deprecated
+@SuppressWarnings("deprecation")
 public class TabPanel extends Composite implements TabListener,
     SourcesTabEvents, HasWidgets, HasAnimation, IndexedPanel.ForIsWidget,
     HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer> {
diff --git a/user/src/com/google/gwt/user/client/ui/ToggleButton.java b/user/src/com/google/gwt/user/client/ui/ToggleButton.java
index 3ce977b..b337f3f 100644
--- a/user/src/com/google/gwt/user/client/ui/ToggleButton.java
+++ b/user/src/com/google/gwt/user/client/ui/ToggleButton.java
@@ -16,6 +16,9 @@
 
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.editor.client.IsEditor;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.editor.client.adapters.TakesValueEditor;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
 import com.google.gwt.event.logical.shared.ValueChangeHandler;
@@ -40,9 +43,11 @@
  * <h3>Example</h3> {@example com.google.gwt.examples.ToggleButtonExample}
  * </p>
  */
-public class ToggleButton extends CustomButton implements HasValue<Boolean> {
+public class ToggleButton extends CustomButton implements HasValue<Boolean>, IsEditor<LeafValueEditor<Boolean>> {
   private static String STYLENAME_DEFAULT = "gwt-ToggleButton";
 
+  private LeafValueEditor<Boolean> editor;
+
   {
     setStyleName(STYLENAME_DEFAULT);
   }
@@ -182,6 +187,13 @@
     return addHandler(handler, ValueChangeEvent.getType());
   }
 
+  public LeafValueEditor<Boolean> asEditor() {
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
+  }
+
   /**
    * Determines whether this button is currently down.
    * 
diff --git a/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java b/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java
index d24f2a3..05d416e 100644
--- a/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java
+++ b/user/src/com/google/gwt/user/client/ui/ValueBoxBase.java
@@ -45,7 +45,6 @@
  * 
  * @param <T> the value type
  */
-@SuppressWarnings("deprecation")
 public class ValueBoxBase<T> extends FocusWidget implements
     HasChangeHandlers, HasName, HasDirectionEstimator,
     HasValue<T>, AutoDirectionHandler.Target, IsEditor<ValueBoxEditor<T>> {
@@ -83,6 +82,7 @@
 
   private final Parser<T> parser;
   private final Renderer<T> renderer;
+  private ValueBoxEditor<T> editor;
   private Event currentEvent;
 
   private boolean valueChangeHandlerInitialized;
@@ -125,7 +125,10 @@
    * Editor framework.
    */
   public ValueBoxEditor<T> asEditor() {
-    return ValueBoxEditor.of(this);
+    if (editor == null) {
+      editor = ValueBoxEditor.of(this);
+    }
+    return editor;
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/client/ui/ValueLabel.java b/user/src/com/google/gwt/user/client/ui/ValueLabel.java
new file mode 100644
index 0000000..92e29db
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/ValueLabel.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2010 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.user.client.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.editor.client.IsEditor;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.editor.client.adapters.TakesValueEditor;
+import com.google.gwt.text.shared.Parser;
+import com.google.gwt.text.shared.Renderer;
+import com.google.gwt.uibinder.client.UiConstructor;
+import com.google.gwt.user.client.TakesValue;
+
+import java.text.ParseException;
+
+/**
+ * A label displaying its value through a renderer.
+ * 
+ * @param <T> the value type.
+ */
+public class ValueLabel<T> extends LabelBase<T> implements TakesValue<T>,
+    IsEditor<LeafValueEditor<T>> {
+
+  /**
+   * Creates a ValueLabel widget that wraps an existing &lt;span&gt; element.
+   * <p>
+   * The ValueLabel's value will be <code>null</code>, whether the element being
+   * wrapped has content or not. Use {@link #wrap(Element, Renderer, Parser)} to
+   * parse the initial element's content to initialize the ValueLabel's value.
+   * <p>
+   * This element must already be attached to the document. If the element is
+   * removed from the document, you must call
+   * {@link RootPanel#detachNow(Widget)}.
+   * 
+   * @param element the element to be wrapped
+   * @param renderer the renderer used to render values into the element
+   */
+  public static <T> ValueLabel<T> wrap(Element element,
+      Renderer<? super T> renderer) {
+    // Assert that the element is attached.
+    assert Document.get().getBody().isOrHasChild(element);
+
+    ValueLabel<T> label = new ValueLabel<T>(element, renderer);
+
+    // Mark it attached and remember it for cleanup.
+    label.onAttach();
+    RootPanel.detachOnWindowClose(label);
+
+    return label;
+  }
+
+  /**
+   * Creates a ValueLabel widget that wraps an existing &lt;span&gt; element.
+   * <p>
+   * The ValueLabel's value will be initialized with the element's content,
+   * passed through the <code>parser</code>.
+   * <p>
+   * This element must already be attached to the document. If the element is
+   * removed from the document, you must call
+   * {@link RootPanel#detachNow(Widget)}.
+   * 
+   * @param element the element to be wrapped
+   * @param renderer the renderer used to render values into the element
+   * @param parser the parser used to initialize the ValueLabel's value from the
+   *          element's content
+   */
+  public static <T> ValueLabel<T> wrap(Element element,
+      Renderer<? super T> renderer, Parser<? extends T> parser)
+      throws ParseException {
+    ValueLabel<T> label = wrap(element, renderer);
+
+    label.setValue(parser.parse(element.getInnerText()));
+
+    // Mark it attached and remember it for cleanup.
+    label.onAttach();
+    RootPanel.detachOnWindowClose(label);
+
+    return label;
+  }
+
+  private final Renderer<? super T> renderer;
+  private T value;
+  private LeafValueEditor<T> editor;
+
+  /**
+   * Creates an empty value label.
+   * 
+   * @param inline
+   * @param renderer
+   */
+  @UiConstructor
+  public ValueLabel(Renderer<? super T> renderer) {
+    super(true);
+    this.renderer = renderer;
+  }
+
+  /**
+   * This constructor may be used by subclasses to explicitly use an existing
+   * element. This element must be either a &lt;span&gt; or a &lt;div&gt;
+   * element.
+   * 
+   * @param element the element to be used
+   */
+  protected ValueLabel(Element element, Renderer<? super T> renderer) {
+    super(element);
+    this.renderer = renderer;
+  }
+
+  public LeafValueEditor<T> asEditor() {
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
+  }
+
+  public T getValue() {
+    return value;
+  }
+
+  public void setValue(T value) {
+    this.value = value;
+    directionalTextHelper.setTextOrHtml(renderer.render(value), false);
+    updateHorizontalAlignment();
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/user/client/ui/ValueListBox.java b/user/src/com/google/gwt/user/client/ui/ValueListBox.java
index 24b8b93..4fffa9a 100644
--- a/user/src/com/google/gwt/user/client/ui/ValueListBox.java
+++ b/user/src/com/google/gwt/user/client/ui/ValueListBox.java
@@ -49,6 +49,7 @@
   private final Renderer<T> renderer;
   private final ProvidesKey<T> keyProvider;
 
+  private TakesValueEditor<T> editor;
   private T value;
 
   public ValueListBox(Renderer<T> renderer) {
@@ -81,7 +82,10 @@
    * Returns a {@link TakesValueEditor} backed by the ValueListBox.
    */
   public TakesValueEditor<T> asEditor() {
-    return TakesValueEditor.of(this);
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
   }
 
   public T getValue() {
diff --git a/user/src/com/google/gwt/user/client/ui/ValuePicker.java b/user/src/com/google/gwt/user/client/ui/ValuePicker.java
index 8003cec..45ced21 100644
--- a/user/src/com/google/gwt/user/client/ui/ValuePicker.java
+++ b/user/src/com/google/gwt/user/client/ui/ValuePicker.java
@@ -47,7 +47,7 @@
     }
 
     @Override
-    public void render(T value, Object viewData, SafeHtmlBuilder sb) {
+    public void render(Context context, T value, SafeHtmlBuilder sb) {
       sb.appendEscaped(renderer.render(value));
     }
   }
@@ -56,6 +56,7 @@
 
   private final CellList<T> cellList;
   private SingleSelectionModel<T> smodel = new SingleSelectionModel<T>();
+  private LeafValueEditor<T> editor;
 
   public ValuePicker(CellList<T> cellList) {
     this.cellList = cellList;
@@ -80,7 +81,10 @@
    * Returns a {@link TakesValueEditor} backed by the ValuePicker.
    */
   public LeafValueEditor<T> asEditor() {
-    return TakesValueEditor.of(this);
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index e0138a0..18082ab 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -55,7 +55,6 @@
    */
   int eventsToSink;
   private boolean attached;
-  @SuppressWarnings("deprecation")
   private HandlerManager handlerManager;
   private Object layoutData;
   private Widget parent;
@@ -74,7 +73,6 @@
    * @param handler the handler
    * @return {@link HandlerRegistration} used to remove the handler
    */
-  @SuppressWarnings("deprecation")
   public final <H extends EventHandler> HandlerRegistration addDomHandler(
       final H handler, DomEvent.Type<H> type) {
     assert handler != null : "handler must not be null";
@@ -91,7 +89,6 @@
    * @param handler the handler
    * @return {@link HandlerRegistration} used to remove the handler
    */
-  @SuppressWarnings("deprecation")
   public final <H extends EventHandler> HandlerRegistration addHandler(
       final H handler, GwtEvent.Type<H> type) {
     return ensureHandlers().addHandler(type, handler);
@@ -101,7 +98,6 @@
     return this;
   }
 
-  @SuppressWarnings("deprecation")
   public void fireEvent(GwtEvent<?> event) {
     if (handlerManager != null) {
       handlerManager.fireEvent(event);
@@ -228,7 +224,6 @@
    *
    * @return the {@link HandlerManager} you want to use
    */
-  @SuppressWarnings("deprecation")
   protected com.google.gwt.event.shared.HandlerManager createHandlerManager() {
     return new com.google.gwt.event.shared.HandlerManager(this);
   }
@@ -272,7 +267,6 @@
    * @param type the event type
    * @return the number of registered handlers
    */
-  @SuppressWarnings("deprecation")
   protected int getHandlerCount(Type<?> type) {
     return handlerManager == null ? 0 : handlerManager.getHandlerCount(type);
   }
@@ -396,13 +390,11 @@
    *
    * @return the handler manager
    * */
-  @SuppressWarnings("deprecation")
   com.google.gwt.event.shared.HandlerManager ensureHandlers() {
     return handlerManager == null ? handlerManager = createHandlerManager()
         : handlerManager;
   }
 
-  @SuppressWarnings("deprecation")
   com.google.gwt.event.shared.HandlerManager getHandlerManager() {
     return handlerManager;
   }
diff --git a/user/src/com/google/gwt/user/datepicker/client/DateBox.java b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
index c9ebf25..3d6b25b 100644
--- a/user/src/com/google/gwt/user/datepicker/client/DateBox.java
+++ b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
@@ -249,6 +249,7 @@
   private final PopupPanel popup;
   private final TextBox box = new TextBox();
   private final DatePicker picker;
+  private LeafValueEditor<Date> editor;
   private Format format;
   private boolean allowDPShow = true;
 
@@ -298,7 +299,10 @@
    * Returns a {@link TakesValueEditor} backed by the DateBox.
    */
   public LeafValueEditor<Date> asEditor() {
-    return TakesValueEditor.of(this);
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/datepicker/client/DatePicker.java b/user/src/com/google/gwt/user/datepicker/client/DatePicker.java
index ddb55a4..439875a 100644
--- a/user/src/com/google/gwt/user/datepicker/client/DatePicker.java
+++ b/user/src/com/google/gwt/user/datepicker/client/DatePicker.java
@@ -253,6 +253,7 @@
   private Date value;
   private Date highlighted;
   private StandardCss css = StandardCss.DEFAULT;
+  private LeafValueEditor<Date> editor;
 
   /**
    * Create a new date picker.
@@ -381,7 +382,10 @@
    * Returns a {@link TakesValueEditor} backed by the DatePicker.
    */
   public LeafValueEditor<Date> asEditor() {
-    return TakesValueEditor.of(this);
+    if (editor == null) {
+      editor = TakesValueEditor.of(this);
+    }
+    return editor;
   }
 
   /**
diff --git a/user/src/com/google/gwt/view/client/CellPreviewEvent.java b/user/src/com/google/gwt/view/client/CellPreviewEvent.java
index d2fcc06..aa486a2 100644
--- a/user/src/com/google/gwt/view/client/CellPreviewEvent.java
+++ b/user/src/com/google/gwt/view/client/CellPreviewEvent.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.view.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.event.shared.EventHandler;
 import com.google.gwt.event.shared.GwtEvent;
@@ -55,39 +56,17 @@
    * @param source the source of the handlers
    * @param nativeEvent the event to preview
    * @param display the {@link HasData} source of the event
-   * @param index the index of the value where the event occurred
+   * @param context the Cell {@link Context}
    * @param value the value where the event occurred
    * @param isCellEditing indicates whether or not the cell is being edited
    * @param isSelectionHandled indicates whether or not selection is handled
    * @return the {@link CellPreviewEvent} that was fired
    */
   public static <T> CellPreviewEvent<T> fire(HasCellPreviewHandlers<T> source,
-      NativeEvent nativeEvent, HasData<T> display, int index, T value,
+      NativeEvent nativeEvent, HasData<T> display, Context context, T value,
       boolean isCellEditing, boolean isSelectionHandled) {
-    return fire(source, nativeEvent, display, index, 0, value, isCellEditing,
-        isSelectionHandled);
-  }
-
-  /**
-   * Fires a cell preview event on all registered handlers in the handler
-   * manager. If no such handlers exist, this implementation will do nothing.
-   * 
-   * @param <T> the old value type
-   * @param source the source of the handlers
-   * @param nativeEvent the event to preview
-   * @param display the {@link HasData} source of the event
-   * @param index the index of the value where the event occurred
-   * @param column the column index
-   * @param value the value where the event occurred
-   * @param isCellEditing indicates whether or not the cell is being edited
-   * @param isSelectionHandled indicates whether or not selection is handled
-   * @return the {@link CellPreviewEvent} that was fired
-   */
-  public static <T> CellPreviewEvent<T> fire(HasCellPreviewHandlers<T> source,
-      NativeEvent nativeEvent, HasData<T> display, int index, int column,
-      T value, boolean isCellEditing, boolean isSelectionHandled) {
     CellPreviewEvent<T> event = new CellPreviewEvent<T>(nativeEvent, display,
-        index, column, value, isCellEditing, isSelectionHandled);
+        context, value, isCellEditing, isSelectionHandled);
     if (TYPE != null) {
       source.fireEvent(event);
     }
@@ -106,9 +85,8 @@
     return TYPE;
   }
 
-  private final int column;
+  private final Context context;
   private final HasData<T> display;
-  private final int index;
   private boolean isCanceled = false;
   private final boolean isCellEditing;
   private final boolean isSelectionHandled;
@@ -120,18 +98,17 @@
    * 
    * @param nativeEvent the event to preview
    * @param display the {@link HasData} source of the event
-   * @param index the index of the value where the event occurred
+   * @param context the Cell {@link Context}
    * @param value the value where the event occurred
    * @param isCellEditing indicates whether or not the cell is being edited
    * @param isSelectionHandled indicates whether or not selection is handled
    */
   protected CellPreviewEvent(NativeEvent nativeEvent, HasData<T> display,
-      int index, int column, T value, boolean isCellEditing,
+      Context context, T value, boolean isCellEditing,
       boolean isSelectionHandled) {
     this.nativeEvent = nativeEvent;
     this.display = display;
-    this.index = index;
-    this.column = column;
+    this.context = context;
     this.value = value;
     this.isCellEditing = isCellEditing;
     this.isSelectionHandled = isSelectionHandled;
@@ -152,7 +129,16 @@
    * @return the column index, or 0 if there is only one column
    */
   public int getColumn() {
-    return column;
+    return context.getColumn();
+  }
+
+  /**
+   * Get the cell {@link Context}.
+   * 
+   * @return the cell {@link Context}
+   */
+  public Context getContext() {
+    return context;
   }
 
   /**
@@ -166,7 +152,7 @@
    * Get the index of the value where the event occurred.
    */
   public int getIndex() {
-    return index;
+    return context.getIndex();
   }
 
   /**
diff --git a/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/ValueCodexHelper.java b/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/ValueCodexHelper.java
new file mode 100644
index 0000000..42c87e3
--- /dev/null
+++ b/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/ValueCodexHelper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 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.autobean.shared;
+
+/**
+ * A no-op super-source version of ValueCodexHelper for web-mode compilations.
+ */
+class ValueCodexHelper {
+  /**
+   * Returns {@code false}.
+   */
+  static boolean canDecode(Class<?> clazz) {
+    return false;
+  }
+}
diff --git a/user/test/com/google/gwt/cell/client/AbstractCellTest.java b/user/test/com/google/gwt/cell/client/AbstractCellTest.java
index 1414369..5520e79 100644
--- a/user/test/com/google/gwt/cell/client/AbstractCellTest.java
+++ b/user/test/com/google/gwt/cell/client/AbstractCellTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 
@@ -27,7 +28,8 @@
     Cell<String> cell = createCell();
     String value = createCellValue();
     Element parent = Document.get().createDivElement();
-    cell.setValue(parent, value, null);
+    Context context = new Context(0, 0, null);
+    cell.setValue(context, parent, value);
     assertEquals(getExpectedInnerHtml(), parent.getInnerHTML());
   }
 
diff --git a/user/test/com/google/gwt/cell/client/ActionCellTest.java b/user/test/com/google/gwt/cell/client/ActionCellTest.java
index b8cdfa7..474375e 100644
--- a/user/test/com/google/gwt/cell/client/ActionCellTest.java
+++ b/user/test/com/google/gwt/cell/client/ActionCellTest.java
@@ -16,6 +16,7 @@
 package com.google.gwt.cell.client;
 
 import com.google.gwt.cell.client.ActionCell.Delegate;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -49,7 +50,8 @@
     Element parent = Document.get().createDivElement();
     NativeEvent event = Document.get().createClickEvent(0, 0, 0, 0, 0, false,
         false, false, false);
-    cell.onBrowserEvent(parent, "test", DEFAULT_KEY, event, null);
+    Context context = new Context(0, 0, DEFAULT_KEY);
+    cell.onBrowserEvent(context, parent, "test", event, null);
     delegate.assertLastObject("test");
   }
 
diff --git a/user/test/com/google/gwt/cell/client/CellTestBase.java b/user/test/com/google/gwt/cell/client/CellTestBase.java
index cdbee13..733b1e5 100644
--- a/user/test/com/google/gwt/cell/client/CellTestBase.java
+++ b/user/test/com/google/gwt/cell/client/CellTestBase.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -41,7 +42,9 @@
   static class MockCell<T> extends AbstractCell<T> {
 
     private final boolean isSelectable;
+    private Context lastContext;
     private T lastEventValue;
+    private Element lastParentElement;
     private final T updateValue;
 
     public MockCell(boolean isSelectable, T updateValue,
@@ -55,19 +58,30 @@
       assertEquals(expected, lastEventValue);
     }
 
+    public void assertLastParentElement(Element expected) {
+      assertEquals(expected, lastParentElement);
+    }
+
+    
     @Override
     public boolean dependsOnSelection() {
       return isSelectable;
     }
 
+    public Context getLastContext() {
+      return lastContext;
+    }
+
     @Override
     public boolean handlesSelection() {
       return isSelectable;
     }
 
     @Override
-    public void onBrowserEvent(Element parent, T value, Object key,
+    public void onBrowserEvent(Context context, Element parent, T value,
         NativeEvent event, ValueUpdater<T> valueUpdater) {
+      lastContext = context;
+      lastParentElement = parent;
       lastEventValue = value;
       if (valueUpdater != null) {
         valueUpdater.update(updateValue);
@@ -75,7 +89,8 @@
     }
 
     @Override
-    public void render(T value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, T value, SafeHtmlBuilder sb) {
+      lastContext = context;
       if (value != null) {
         sb.appendEscaped(String.valueOf(value));
       }
@@ -139,7 +154,20 @@
     Cell<T> cell = createCell();
     T value = createCellValue();
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    cell.render(value, null, sb);
+    Context context = new Context(0, 0, null);
+    cell.render(context, value, sb);
+    assertEquals(getExpectedInnerHtml(), sb.toSafeHtml().asString());
+  }
+
+  /**
+   * Test rendering the cell with a negative index is handled.
+   */
+  public void testRenderNegativeIndex() {
+    Cell<T> cell = createCell();
+    T value = createCellValue();
+    SafeHtmlBuilder sb = new SafeHtmlBuilder();
+    Context context = new Context(-1, -1, null);
+    cell.render(context, value, sb);
     assertEquals(getExpectedInnerHtml(), sb.toSafeHtml().asString());
   }
 
@@ -149,7 +177,8 @@
   public void testRenderNull() {
     Cell<T> cell = createCell();
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    cell.render(null, null, sb);
+    Context context = new Context(0, 0, null);
+    cell.render(context, null, sb);
     assertEquals(getExpectedInnerHtmlNull(), sb.toSafeHtml().asString());
   }
 
@@ -225,7 +254,8 @@
       public void onBrowserEvent(Event event) {
         try {
           DOM.setEventListener(parent, null);
-          createCell().onBrowserEvent(parent, value, DEFAULT_KEY, event,
+          Context context = new Context(0, 0, DEFAULT_KEY);
+          createCell().onBrowserEvent(context, parent, value, event,
               valueUpdater);
           parent.removeFromParent();
         } catch (Exception e) {
diff --git a/user/test/com/google/gwt/cell/client/CompositeCellTest.java b/user/test/com/google/gwt/cell/client/CompositeCellTest.java
index a56e6ef..a40461c 100644
--- a/user/test/com/google/gwt/cell/client/CompositeCellTest.java
+++ b/user/test/com/google/gwt/cell/client/CompositeCellTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -109,7 +110,8 @@
     // Add an event listener.
     EventListener listener = new EventListener() {
       public void onBrowserEvent(Event event) {
-        cell.onBrowserEvent(parent, "test", DEFAULT_KEY, event, null);
+        Context context = new Context(3, 4, "key");
+        cell.onBrowserEvent(context, parent, "test", event, null);
       }
     };
     DOM.sinkEvents(parent, Event.ONCLICK);
@@ -120,6 +122,11 @@
         false, false, false);
     Element.as(parent.getChild(1)).dispatchEvent(event);
     innerCell.assertLastEventValue("test-1");
+    innerCell.assertLastParentElement(Element.as(parent.getChild(1)));
+    Context innerContext = innerCell.getLastContext();
+    assertEquals("key", innerContext.getKey());
+    assertEquals(3, innerContext.getIndex());
+    assertEquals(4, innerContext.getColumn());
 
     // Remove the element and event listener.
     DOM.setEventListener(parent, null);
@@ -130,7 +137,8 @@
     Cell<String> cell = createCell();
     Element parent = Document.get().createDivElement();
     parent.setInnerHTML(getExpectedInnerHtml());
-    cell.setValue(parent, "test", null);
+    Context context = new Context( 0, 0, null);
+    cell.setValue(context, parent, "test");
 
     assertEquals(3, parent.getChildCount());
     assertEquals("test-0", Element.as(parent.getChild(0)).getInnerHTML());
diff --git a/user/test/com/google/gwt/cell/client/EditTextCellTest.java b/user/test/com/google/gwt/cell/client/EditTextCellTest.java
index 8925a5d..c088c71 100644
--- a/user/test/com/google/gwt/cell/client/EditTextCellTest.java
+++ b/user/test/com/google/gwt/cell/client/EditTextCellTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.cell.client.EditTextCell.ViewData;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
@@ -35,7 +36,8 @@
     ViewData viewData = new ViewData("originalValue");
     viewData.setText("newValue");
     cell.setViewData(DEFAULT_KEY, viewData);
-    cell.edit(parent, "originalValue", DEFAULT_KEY);
+    Context context = new Context(0, 0, DEFAULT_KEY);
+    cell.edit(context, parent, "originalValue");
 
     // Verify the input element.
     Element child = parent.getFirstChildElement();
@@ -130,7 +132,8 @@
     viewData.setEditing(false);
     cell.setViewData(DEFAULT_KEY, viewData);
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    cell.render("originalValue", DEFAULT_KEY, sb);
+    Context context = new Context(0, 0, DEFAULT_KEY);
+    cell.render(context, "originalValue", sb);
     assertEquals("newValue", sb.toSafeHtml().asString());
   }
 
diff --git a/user/test/com/google/gwt/cell/client/EditableCellTestBase.java b/user/test/com/google/gwt/cell/client/EditableCellTestBase.java
index 1eddda7..f1c6230 100644
--- a/user/test/com/google/gwt/cell/client/EditableCellTestBase.java
+++ b/user/test/com/google/gwt/cell/client/EditableCellTestBase.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -39,7 +40,8 @@
     T value = createCellValue();
     cell.setViewData(DEFAULT_KEY, createCellViewData());
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    cell.render(value, DEFAULT_KEY, sb);
+    Context context = new Context(0, 0, DEFAULT_KEY);
+    cell.render(context, value, sb);
     String expectedInnerHtmlViewData = getExpectedInnerHtmlViewData();
     String asString = sb.toSafeHtml().asString();
     assertEquals(expectedInnerHtmlViewData, asString);
@@ -96,7 +98,8 @@
       public void onBrowserEvent(Event event) {
         try {
           DOM.setEventListener(parent, null);
-          cell.onBrowserEvent(parent, value, DEFAULT_KEY, event, valueUpdater);
+          Context context = new Context(0, 0, DEFAULT_KEY);
+          cell.onBrowserEvent(context, parent, value, event, valueUpdater);
           parent.removeFromParent();
         } catch (Exception e) {
           // We are in an event loop, so events may not propagate out to JUnit.
diff --git a/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java b/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java
index 79c656a..054b751 100644
--- a/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java
+++ b/user/test/com/google/gwt/cell/client/IconCellDecoratorTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
@@ -62,7 +63,8 @@
 
     // Render the cell.
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    cell.render("helloworld", null, sb);
+    Context context = new Context(0, 0, null);
+    cell.render(context, "helloworld", sb);
 
     // Compare the expected render string.
     String expected = "<div style=\"position:relative;padding-left:64px;\">";
@@ -88,7 +90,8 @@
     parent.setInnerHTML(getExpectedInnerHtml());
     assertEquals("helloworld",
         Element.as(parent.getFirstChildElement().getChild(1)).getInnerHTML());
-    cell.setValue(parent, "test", null);
+    Context context = new Context(0, 0, null);
+    cell.setValue(context, parent, "test");
     assertEquals("test",
         Element.as(parent.getFirstChildElement().getChild(1)).getInnerHTML());
   }
diff --git a/user/test/com/google/gwt/cell/client/ImageLoadingCellTest.java b/user/test/com/google/gwt/cell/client/ImageLoadingCellTest.java
index 8d0d606..87d7beb 100644
--- a/user/test/com/google/gwt/cell/client/ImageLoadingCellTest.java
+++ b/user/test/com/google/gwt/cell/client/ImageLoadingCellTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.cell.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.ImageElement;
@@ -30,7 +31,8 @@
     Cell<String> cell = createCell();
     String value = createCellValue();
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    cell.render(value, null, sb);
+    Context context = new Context(0, 0, null);
+    cell.render(context, value, sb);
 
     // Render the html.
     Element elem = Document.get().createDivElement();
@@ -45,6 +47,26 @@
   }
 
   @Override
+  public void testRenderNegativeIndex() {
+    Cell<String> cell = createCell();
+    String value = createCellValue();
+    SafeHtmlBuilder sb = new SafeHtmlBuilder();
+    Context context = new Context(-1, -1, null);
+    cell.render(context, value, sb);
+ 
+    // Render the html.
+    Element elem = Document.get().createDivElement();
+    elem.setInnerHTML(sb.toSafeHtml().asString());
+
+    // Verify the image.
+    assertEquals(2, elem.getChildCount());
+    Element imgWrapper = elem.getChild(1).cast();
+    ImageElement img = imgWrapper.getFirstChildElement().cast();
+    assertEquals("img", img.getTagName().toLowerCase());
+    assertTrue(img.getSrc().toLowerCase().endsWith("test.png"));
+  }
+
+  @Override
   protected Cell<String> createCell() {
     return new ImageLoadingCell();
   }
diff --git a/user/test/com/google/gwt/editor/client/adapters/ListEditorWrapperTest.java b/user/test/com/google/gwt/editor/client/adapters/ListEditorWrapperTest.java
index b787449..8c8c2a1 100644
--- a/user/test/com/google/gwt/editor/client/adapters/ListEditorWrapperTest.java
+++ b/user/test/com/google/gwt/editor/client/adapters/ListEditorWrapperTest.java
@@ -44,6 +44,9 @@
 
     Object o1 = new Object();
     wrapper.add(o1);
+    assertEquals(0, backing.size());
+    wrapper.flush();
+    assertEquals(1, backing.size());
     assertSame(o1, backing.get(0));
     FakeLeafValueEditor<Object> editor1 = wrapper.getEditors().get(0);
     assertSame(o1, editor1.getValue());
@@ -52,6 +55,9 @@
 
     Object o0 = new Object();
     wrapper.add(0, o0);
+    assertEquals(1, backing.size());
+    wrapper.flush();
+    assertEquals(2, backing.size());
     assertSame(o0, backing.get(0));
     assertSame(o1, backing.get(1));
     FakeLeafValueEditor<Object> editor0 = wrapper.getEditors().get(0);
@@ -112,6 +118,9 @@
     assertSame(o2, e2.getValue());
 
     wrapper.remove(1);
+    assertEquals(Arrays.asList(o0, o1, o2), backing);
+    assertEquals(Arrays.asList(e0, e2), wrapper.getEditors());
+    wrapper.flush();
     assertEquals(Arrays.asList(o0, o2), backing);
     assertEquals(Arrays.asList(e0, e2), wrapper.getEditors());
     assertFalse(chain.isAttached(e1));
@@ -119,6 +128,8 @@
     assertEquals(1, source.getLastKnownPosition(e2));
 
     wrapper.set(1, o1);
+    assertEquals(Arrays.asList(o0, o2), backing);
+    wrapper.flush();
     assertEquals(Arrays.asList(o0, o1), backing);
     // Re-use existing editor
     assertEquals(Arrays.asList(e0, e2), wrapper.getEditors());
diff --git a/user/test/com/google/gwt/requestfactory/RequestFactoryJreSuite.java b/user/test/com/google/gwt/requestfactory/RequestFactoryJreSuite.java
index c158123..61d7071 100644
--- a/user/test/com/google/gwt/requestfactory/RequestFactoryJreSuite.java
+++ b/user/test/com/google/gwt/requestfactory/RequestFactoryJreSuite.java
@@ -21,6 +21,7 @@
 import com.google.gwt.requestfactory.server.LocatorJreTest;
 import com.google.gwt.requestfactory.server.RequestFactoryInterfaceValidatorTest;
 import com.google.gwt.requestfactory.server.RequestFactoryJreTest;
+import com.google.gwt.requestfactory.server.RequestFactoryUnicodeEscapingJreTest;
 import com.google.gwt.requestfactory.shared.impl.SimpleEntityProxyIdTest;
 
 import junit.framework.Test;
@@ -40,6 +41,7 @@
     suite.addTestSuite(SimpleEntityProxyIdTest.class);
     suite.addTestSuite(RequestFactoryInterfaceValidatorTest.class);
     suite.addTestSuite(RequestFactoryModelTest.class);
+    suite.addTestSuite(RequestFactoryUnicodeEscapingJreTest.class);
     return suite;
   }
 }
diff --git a/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java b/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java
index cbb5558..884d005 100644
--- a/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java
+++ b/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java
@@ -20,6 +20,7 @@
 import com.google.gwt.requestfactory.client.RequestFactoryExceptionHandlerTest;
 import com.google.gwt.requestfactory.client.RequestFactoryPolymorphicTest;
 import com.google.gwt.requestfactory.client.RequestFactoryTest;
+import com.google.gwt.requestfactory.client.RequestFactoryUnicodeEscapingTest;
 import com.google.gwt.requestfactory.client.ui.EditorTest;
 import com.google.gwt.requestfactory.shared.ComplexKeysTest;
 import com.google.gwt.requestfactory.shared.LocatorTest;
@@ -40,6 +41,7 @@
     suite.addTestSuite(RequestFactoryTest.class);
     suite.addTestSuite(RequestFactoryExceptionHandlerTest.class);
     suite.addTestSuite(RequestFactoryPolymorphicTest.class);
+    suite.addTestSuite(RequestFactoryUnicodeEscapingTest.class);
     return suite;
   }
 }
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryExceptionHandlerTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryExceptionHandlerTest.java
index b05e36b..a8b9371 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryExceptionHandlerTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryExceptionHandlerTest.java
@@ -25,6 +25,8 @@
  */
 public class RequestFactoryExceptionHandlerTest extends RequestFactoryTest {
 
+  private static final int DELAY_TEST_FINISH = 5000;
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.requestfactory.RequestFactoryExceptionHandlerTest";
@@ -32,7 +34,7 @@
 
   @Override
   public void testServerFailureCheckedException() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest context = req.simpleFooRequest();
     SimpleFooProxy rayFoo = context.create(SimpleFooProxy.class);
     final Request<SimpleFooProxy> persistRay = context.persistAndReturnSelf().using(
@@ -46,7 +48,7 @@
 
   @Override
   public void testServerFailureRuntimeException() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest context = req.simpleFooRequest();
     SimpleFooProxy rayFoo = context.create(SimpleFooProxy.class);
     final Request<SimpleFooProxy> persistRay = context.persistAndReturnSelf().using(
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
index b512857..5d0cb95 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
@@ -32,11 +32,16 @@
 import com.google.gwt.requestfactory.shared.Violation;
 import com.google.gwt.requestfactory.shared.impl.SimpleEntityProxyId;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Time;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -172,7 +177,7 @@
     }
   }
 
-  private static final int DELAY_TEST_FINISH = 5000;
+  private static final int DELAY_TEST_FINISH = 10 * 1000;
 
   public <T extends EntityProxy> void assertContains(Collection<T> col, T value) {
     EntityProxyId<?> lookFor = value.stableId();
@@ -517,6 +522,41 @@
     });
   }
 
+  /**
+   * Check default value, a newly-set value, and a null value.
+   */
+  public void testEnumProperty() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().findSimpleFooById(999L).fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            assertEquals(SimpleEnum.FOO, response.getEnumField());
+            SimpleFooRequest ctx = simpleFooRequest();
+            response = ctx.edit(response);
+            response.setEnumField(SimpleEnum.BAR);
+            ctx.persistAndReturnSelf().using(response).fire(
+                new Receiver<SimpleFooProxy>() {
+                  @Override
+                  public void onSuccess(SimpleFooProxy response) {
+                    assertEquals(SimpleEnum.BAR, response.getEnumField());
+                    SimpleFooRequest ctx = simpleFooRequest();
+                    response = ctx.edit(response);
+                    response.setEnumField(null);
+                    ctx.persistAndReturnSelf().using(response).fire(
+                        new Receiver<SimpleFooProxy>() {
+                          @Override
+                          public void onSuccess(SimpleFooProxy response) {
+                            assertNull(response.getEnumField());
+                            finishTestAndReset();
+                          }
+                        });
+                  }
+                });
+          }
+        });
+  }
+
   public void testFetchEntity() {
     delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
@@ -581,7 +621,7 @@
   }
 
   public void testFindFindEdit() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
     EntityProxyChange.registerForProxyType(req.getEventBus(),
@@ -687,6 +727,7 @@
 
   public void testHistoryToken() {
     delayTestFinish(DELAY_TEST_FINISH);
+
     SimpleBarRequest context = simpleBarRequest();
     final SimpleBarProxy foo = context.create(SimpleBarProxy.class);
     final EntityProxyId<SimpleBarProxy> futureId = foo.stableId();
@@ -726,10 +767,22 @@
     });
   }
 
+  public void testInstanceServiceRequest() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    req.instanceServiceRequest().add(5).fire(new Receiver<Integer>() {
+      @Override
+      public void onSuccess(Integer response) {
+        assertEquals(10, (int) response);
+        finishTestAndReset();
+      }
+    });
+  }
+
   /**
    * Make sure our stock RF logging service keeps receiving.
    */
   public void testLoggingService() {
+    delayTestFinish(DELAY_TEST_FINISH);
     String logRecordJson = new StringBuilder("{") //
     .append("\"level\": \"ALL\", ") //
     .append("\"loggerName\": \"logger\", ") //
@@ -1121,6 +1174,11 @@
     });
   }
 
+  /*
+   * TODO: all these tests should check the final values. It will be easy when
+   * we have better persistence than the singleton pattern.
+   */
+
   /**
    * Ensure that a relationship can be set up between two newly-created objects.
    */
@@ -1147,11 +1205,6 @@
   }
 
   /*
-   * TODO: all these tests should check the final values. It will be easy when
-   * we have better persistence than the singleton pattern.
-   */
-
-  /*
    * Find Entity2 Create Entity, Persist Entity Relate Entity2 to Entity Persist
    * Entity
    */
@@ -1383,6 +1436,11 @@
         });
   }
 
+  /*
+   * TODO: all these tests should check the final values. It will be easy when
+   * we have better persistence than the singleton pattern.
+   */
+
   public void testPersistValueList() {
     delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
@@ -1446,11 +1504,6 @@
    * TODO: all these tests should check the final values. It will be easy when
    * we have better persistence than the singleton pattern.
    */
-
-  /*
-   * TODO: all these tests should check the final values. It will be easy when
-   * we have better persistence than the singleton pattern.
-   */
   public void testPersistValueListRemove() {
     delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
@@ -1753,20 +1806,71 @@
     });
   }
 
+  public void testPrimitiveListBigDecimalAsParameter() {
+    delayTestFinish(DELAY_TEST_FINISH);
+
+    // Keep these values in sync with SimpleFoo.processBigIntegerList
+    final List<BigDecimal> testList = new ArrayList<BigDecimal>();
+    testList.add(BigDecimal.TEN);
+    testList.add(new BigDecimal("12345.6789") {
+      // This is an anonymous subtype
+    });
+    simpleFooRequest().processBigDecimalList(testList).fire(
+        new Receiver<List<BigDecimal>>() {
+          @Override
+          public void onSuccess(List<BigDecimal> response) {
+            // Check upcasted values only
+            assertEquals(testList, response);
+            finishTestAndReset();
+          }
+        });
+  }
+
+  public void testPrimitiveListBigIntegerAsParameter() {
+    delayTestFinish(DELAY_TEST_FINISH);
+
+    // Keep these values in sync with SimpleFoo.processBigIntegerList
+    final List<BigInteger> testList = new ArrayList<BigInteger>();
+    testList.add(BigInteger.TEN);
+    testList.add(new BigInteger("12345") {
+      // This is an anonymous subtype
+    });
+    simpleFooRequest().processBigIntegerList(testList).fire(
+        new Receiver<List<BigInteger>>() {
+          @Override
+          public void onSuccess(List<BigInteger> response) {
+            // Check upcasted values only
+            assertEquals(testList, response);
+            finishTestAndReset();
+          }
+        });
+  }
+
+  @SuppressWarnings("deprecation")
   public void testPrimitiveListDateAsParameter() {
     delayTestFinish(DELAY_TEST_FINISH);
 
-    @SuppressWarnings("deprecation")
-    final Date date = new Date(90, 0, 1);
-    Request<Date> procReq = simpleFooRequest().processDateList(
-        Arrays.asList(date));
-    procReq.fire(new Receiver<Date>() {
-      @Override
-      public void onSuccess(Date response) {
-        assertEquals(date, response);
-        finishTestAndReset();
-      }
-    });
+    // Keep these values in sync with SimpleFoo.processDateList
+    Date date = new Date(90, 0, 1);
+    java.sql.Date sqlDate = new java.sql.Date(90, 0, 2);
+    Time sqlTime = new Time(1, 2, 3);
+    Timestamp sqlTimestamp = new Timestamp(12345L);
+    final List<Date> testList = Arrays.asList(date, sqlDate, sqlTime,
+        sqlTimestamp);
+    simpleFooRequest().processDateList(testList).fire(
+        new Receiver<List<Date>>() {
+          @Override
+          public void onSuccess(List<Date> response) {
+            // Check upcasted values only
+            assertEquals(testList.size(), response.size());
+            Iterator<Date> expected = testList.iterator();
+            Iterator<Date> actual = response.iterator();
+            while (expected.hasNext()) {
+              assertEquals(expected.next().getTime(), actual.next().getTime());
+            }
+            finishTestAndReset();
+          }
+        });
   }
 
   public void testPrimitiveListEnumAsParameter() {
@@ -1886,6 +1990,8 @@
   }
 
   public void testServerFailureCheckedException() {
+    delayTestFinish(DELAY_TEST_FINISH);
+
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
     final Request<SimpleFooProxy> persistRequest = context.persistAndReturnSelf().using(
@@ -2035,26 +2141,6 @@
   }
 
   /**
-   * We provide a simple UserInformation class to give GAE developers a hand,
-   * and other developers a hint. Make sure RF doesn't break it (it relies on
-   * server side upcasting, and a somewhat sleazey reflective lookup mechanism
-   * in a static method on UserInformation).
-   */
-  public void testUserInfo() {
-    req.userInformationRequest().getCurrentUserInformation("").fire(
-        new Receiver<UserInformationProxy>() {
-          @Override
-          public void onSuccess(UserInformationProxy response) {
-            response = checkSerialization(response);
-            assertEquals("Dummy Email", response.getEmail());
-            assertEquals("Dummy User", response.getName());
-            assertEquals("", response.getLoginUrl());
-            assertEquals("", response.getLogoutUrl());
-          }
-        });
-  }
-
-  /**
    * Check if a graph of unpersisted objects can be echoed.
    */
   public void testUnpersistedEchoComplexGraph() {
@@ -2194,6 +2280,28 @@
         });
   }
 
+  /**
+   * We provide a simple UserInformation class to give GAE developers a hand,
+   * and other developers a hint. Make sure RF doesn't break it (it relies on
+   * server side upcasting, and a somewhat sleazey reflective lookup mechanism
+   * in a static method on UserInformation).
+   */
+  public void testUserInfo() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    req.userInformationRequest().getCurrentUserInformation("").fire(
+        new Receiver<UserInformationProxy>() {
+          @Override
+          public void onSuccess(UserInformationProxy response) {
+            response = checkSerialization(response);
+            assertEquals("Dummy Email", response.getEmail());
+            assertEquals("Dummy User", response.getName());
+            assertEquals("", response.getLoginUrl());
+            assertEquals("", response.getLogoutUrl());
+            finishTestAndReset();
+          }
+        });
+  }
+
   public void testValueObjectCreateSetRetrieveUpdate() {
     delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest req = simpleFooRequest();
@@ -2335,6 +2443,48 @@
     checkEqualityAndHashcode(a, b);
   }
 
+  /**
+   * Since a ValueProxy cannot be passed into RequestContext edit, a proxy
+   * returned from a service method should be mutable by default.
+   */
+  public void testValueObjectReturnedFromRequestIsImmutable() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().returnValueProxy().fire(
+        new Receiver<SimpleValueProxy>() {
+          @Override
+          public void onSuccess(SimpleValueProxy a) {
+            a = checkSerialization(a);
+            try {
+              a.setNumber(77);
+              fail();
+            } catch (IllegalStateException expected) {
+            }
+            try {
+              // Ensure Dates comply with ValueProxy mutation behaviors
+              a.getDate().setTime(1);
+              fail();
+            } catch (IllegalStateException expected) {
+            }
+            SimpleFooRequest ctx = simpleFooRequest();
+            final SimpleValueProxy toCheck = ctx.edit(a);
+            toCheck.setNumber(77);
+            toCheck.getDate().setTime(1);
+            ctx.returnValueProxy().fire(new Receiver<SimpleValueProxy>() {
+              @Override
+              public void onSuccess(SimpleValueProxy b) {
+                b = checkSerialization(b);
+                b = simpleFooRequest().edit(b);
+                // Now check that same value is equal across contexts
+                b.setNumber(77);
+                b.setDate(new Date(1));
+                checkEqualityAndHashcode(toCheck, b);
+                finishTestAndReset();
+              }
+            });
+          }
+        });
+  }
+
   public void testValueObjectViolationsOnCreate() {
     delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest req = simpleFooRequest();
@@ -2395,48 +2545,6 @@
         });
   }
 
-  /**
-   * Since a ValueProxy cannot be passed into RequestContext edit, a proxy
-   * returned from a service method should be mutable by default.
-   */
-  public void testValueObjectReturnedFromRequestIsImmutable() {
-    delayTestFinish(DELAY_TEST_FINISH);
-    simpleFooRequest().returnValueProxy().fire(
-        new Receiver<SimpleValueProxy>() {
-          @Override
-          public void onSuccess(SimpleValueProxy a) {
-            a = checkSerialization(a);
-            try {
-              a.setNumber(77);
-              fail();
-            } catch (IllegalStateException expected) {
-            }
-            try {
-              // Ensure Dates comply with ValueProxy mutation behaviors
-              a.getDate().setTime(1);
-              fail();
-            } catch (IllegalStateException expected) {
-            }
-            SimpleFooRequest ctx = simpleFooRequest();
-            final SimpleValueProxy toCheck = ctx.edit(a);
-            toCheck.setNumber(77);
-            toCheck.getDate().setTime(1);
-            ctx.returnValueProxy().fire(new Receiver<SimpleValueProxy>() {
-              @Override
-              public void onSuccess(SimpleValueProxy b) {
-                b = checkSerialization(b);
-                b = simpleFooRequest().edit(b);
-                // Now check that same value is equal across contexts
-                b.setNumber(77);
-                b.setDate(new Date(1));
-                checkEqualityAndHashcode(toCheck, b);
-                finishTestAndReset();
-              }
-            });
-          }
-        });
-  }
-
   public void testViolationAbsent() {
     delayTestFinish(DELAY_TEST_FINISH);
 
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java
new file mode 100644
index 0000000..44de4ed
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryUnicodeEscapingTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2010 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.requestfactory.client;
+
+import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.requestfactory.shared.ServerFailure;
+import com.google.gwt.user.client.rpc.UnicodeEscapingService.InvalidCharacterException;
+import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Runs through a portion of the Basic Multilingual Plane.
+ */
+public class RequestFactoryUnicodeEscapingTest extends RequestFactoryTestBase {
+  private static final int TEST_FINISH_DELAY_MS = 5000;
+  private final UnicodeEscapingTest test = new UnicodeEscapingTest() {
+
+    @Override
+    protected void clientToServerVerifyRange(int start, final int end,
+        final int size, final int step) throws InvalidCharacterException {
+      current = start;
+      int blockEnd = Math.min(end, current + size);
+      req.unicodeTestRequest().verifyStringContainingCharacterRange(current,
+          blockEnd, getStringContainingCharacterRange(start, blockEnd)).fire(
+          new Receiver<Void>() {
+            List<ServerFailure> fails = new ArrayList<ServerFailure>();
+
+            @Override
+            public void onFailure(ServerFailure error) {
+              fails.add(error);
+              onSuccess(null);
+            }
+
+            @Override
+            public void onSuccess(Void response) {
+              current += step;
+              if (current < end) {
+                delayTestFinish(TEST_FINISH_DELAY_MS);
+                int blockEnd = Math.min(end, current + size);
+                req.unicodeTestRequest().verifyStringContainingCharacterRange(
+                    current, blockEnd,
+                    getStringContainingCharacterRange(current, blockEnd)).fire(
+                    this);
+              } else if (!fails.isEmpty()) {
+                StringBuilder msg = new StringBuilder();
+                for (ServerFailure error : fails) {
+                  msg.append(error.getMessage()).append("\n");
+                }
+                throw new RuntimeException(msg.toString());
+              } else {
+                finishTest();
+              }
+            }
+          });
+    }
+
+    @Override
+    protected void serverToClientVerify(int start, final int end,
+        final int size, final int step) {
+      current = start;
+      req.unicodeTestRequest().getStringContainingCharacterRange(start,
+          Math.min(end, current + size)).fire(new Receiver<String>() {
+        List<ServerFailure> fails = new ArrayList<ServerFailure>();
+
+        @Override
+        public void onFailure(ServerFailure error) {
+          fails.add(error);
+          nextBatch();
+        }
+
+        @Override
+        public void onSuccess(String response) {
+          try {
+            verifyStringContainingCharacterRange(current,
+                Math.min(end, current + size), response);
+          } catch (InvalidCharacterException e) {
+            fails.add(new ServerFailure(e.getMessage(), null, null));
+          }
+          nextBatch();
+        }
+
+        private void nextBatch() {
+          current += step;
+          if (current < end) {
+            delayTestFinish(TEST_FINISH_DELAY_MS);
+            req.unicodeTestRequest().getStringContainingCharacterRange(current,
+                Math.min(end, current + size)).fire(this);
+          } else if (!fails.isEmpty()) {
+            StringBuilder msg = new StringBuilder();
+            for (ServerFailure t : fails) {
+              msg.append(t.getMessage()).append("\n");
+            }
+            throw new RuntimeException(msg.toString());
+          } else {
+            finishTest();
+          }
+        }
+      });
+    }
+  };
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.requestfactory.RequestFactorySuite";
+  }
+
+  public void testClientToServerBMPHigh() throws InvalidCharacterException {
+    test.testClientToServerBMPHigh();
+  }
+
+  public void testClientToServerBMPLow() throws InvalidCharacterException {
+    test.testClientToServerBMPLow();
+  }
+
+  public void testClientToServerNonBMP() throws InvalidCharacterException {
+    test.testClientToServerNonBMP();
+  }
+
+  public void testServerToClientBMP() {
+    test.testServerToClientBMP();
+  }
+
+  public void testServerToClientNonBMP() {
+    test.testServerToClientNonBMP();
+  }
+
+}
diff --git a/user/test/com/google/gwt/requestfactory/rebind/model/RequestFactoryModelTest.java b/user/test/com/google/gwt/requestfactory/rebind/model/RequestFactoryModelTest.java
index 3e160e5..ce68390 100644
--- a/user/test/com/google/gwt/requestfactory/rebind/model/RequestFactoryModelTest.java
+++ b/user/test/com/google/gwt/requestfactory/rebind/model/RequestFactoryModelTest.java
@@ -28,12 +28,14 @@
 import com.google.gwt.requestfactory.server.TestContextImpl;
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.InstanceRequest;
+import com.google.gwt.requestfactory.shared.Locator;
 import com.google.gwt.requestfactory.shared.ProxyFor;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.requestfactory.shared.Request;
 import com.google.gwt.requestfactory.shared.RequestContext;
 import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.requestfactory.shared.ServiceLocator;
 import com.google.gwt.requestfactory.shared.ValueProxy;
 
 import junit.framework.TestCase;
@@ -269,8 +271,10 @@
         new EmptyMockJavaResource(Iterable.class),
         new EmptyMockJavaResource(EntityProxy.class),
         new EmptyMockJavaResource(InstanceRequest.class),
+        new EmptyMockJavaResource(Locator.class),
         new EmptyMockJavaResource(RequestFactory.class),
         new EmptyMockJavaResource(Receiver.class),
+        new EmptyMockJavaResource(ServiceLocator.class),
         new EmptyMockJavaResource(ValueProxy.class),
 
         new RealJavaResource(Request.class),
diff --git a/user/test/com/google/gwt/requestfactory/server/InstanceService.java b/user/test/com/google/gwt/requestfactory/server/InstanceService.java
new file mode 100644
index 0000000..9d9c940
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/server/InstanceService.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010 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.requestfactory.server;
+
+/**
+ * A service API that doesn't have static methods and that can't be
+ * default-instantiated.
+ */
+public interface InstanceService {
+  Integer add(int value);
+}
diff --git a/user/test/com/google/gwt/requestfactory/server/InstanceServiceImpl.java b/user/test/com/google/gwt/requestfactory/server/InstanceServiceImpl.java
new file mode 100644
index 0000000..e2dd226
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/server/InstanceServiceImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 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.requestfactory.server;
+
+/**
+ * A service API that doesn't have static methods and that can't be
+ * default-instantiated.
+ */
+public class InstanceServiceImpl implements InstanceService {
+  private final int base;
+
+  public InstanceServiceImpl(int base) {
+    this.base = base;
+  }
+
+  public Integer add(int value) {
+    return base + value;
+  }
+}
diff --git a/user/test/com/google/gwt/requestfactory/server/InstanceServiceLocator.java b/user/test/com/google/gwt/requestfactory/server/InstanceServiceLocator.java
new file mode 100644
index 0000000..e5a5b9e
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/server/InstanceServiceLocator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 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.requestfactory.server;
+
+import com.google.gwt.requestfactory.shared.ServiceLocator;
+
+/**
+ * Demonstrates instance-based service objects.
+ */
+public class InstanceServiceLocator implements ServiceLocator {
+
+  public Object getInstance(Class<?> clazz) {
+    assert InstanceService.class.equals(clazz);
+    return clazz.cast(new InstanceServiceImpl(5));
+  }
+}
diff --git a/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java b/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java
index eb87261..1c6c003 100644
--- a/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java
+++ b/user/test/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidatorTest.java
@@ -19,12 +19,13 @@
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.InstanceRequest;
 import com.google.gwt.requestfactory.shared.Locator;
-import com.google.gwt.requestfactory.shared.LocatorFor;
 import com.google.gwt.requestfactory.shared.ProxyFor;
+import com.google.gwt.requestfactory.shared.ProxyForName;
 import com.google.gwt.requestfactory.shared.Request;
 import com.google.gwt.requestfactory.shared.RequestContext;
 import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.requestfactory.shared.ServiceName;
 import com.google.gwt.requestfactory.shared.SimpleRequestFactory;
 import com.google.gwt.requestfactory.shared.ValueProxy;
 import com.google.gwt.requestfactory.shared.impl.FindRequest;
@@ -86,6 +87,10 @@
     int foo(int a) {
       return 0;
     }
+
+    java.sql.Date getSqlDate() {
+      return null;
+    }
   }
 
   @ProxyFor(Domain.class)
@@ -110,11 +115,17 @@
       return 0;
     }
   }
+
   @ProxyFor(DomainWithOverloads.class)
   interface DomainWithOverloadsProxy extends EntityProxy {
     void foo();
   }
 
+  @ProxyFor(Domain.class)
+  interface DomainWithSqlDateProxy extends EntityProxy {
+    java.sql.Date getSqlDate();
+  }
+
   class Foo {
   }
 
@@ -180,8 +191,7 @@
     }
   }
 
-  @ProxyFor(LocatorEntity.class)
-  @LocatorFor(LocatorEntityLocator.class)
+  @ProxyFor(value = LocatorEntity.class, locator = LocatorEntityLocator.class)
   interface LocatorEntityProxy extends EntityProxy {
   }
 
@@ -228,6 +238,13 @@
     Request<Integer> doesNotExist(int a);
   }
 
+  @ProxyFor(Domain.class)
+  @ProxyForName("Domain")
+  @Service(Domain.class)
+  @ServiceName("Domain")
+  interface TooManyAnnotations extends RequestContext {
+  }
+
   static class UnexpectedIdAndVersionDomain {
     Random getId() {
       return null;
@@ -263,6 +280,14 @@
   };
 
   /**
+   * Test that subclasses of {@code java.util.Date} are not transportable.
+   */
+  public void testDateSubclass() {
+    v.validateEntityProxy(DomainWithSqlDateProxy.class.getName());
+    assertTrue(v.isPoisoned());
+  }
+
+  /**
    * Test the {@link FindRequest} context used to implement find().
    */
   public void testFindRequestContext() {
@@ -349,6 +374,11 @@
     assertFalse(v.isPoisoned());
   }
 
+  public void testTooManyAnnotations() {
+    v.validateRequestContext(TooManyAnnotations.class.getName());
+    assertTrue(v.isPoisoned());
+  }
+
   public void testUnexpectedIdAndVersion() {
     v.validateEntityProxy(UnexpectedIdAndVersionProxy.class.getName());
     assertTrue(v.isPoisoned());
diff --git a/user/test/com/google/gwt/requestfactory/server/RequestFactoryUnicodeEscapingJreTest.java b/user/test/com/google/gwt/requestfactory/server/RequestFactoryUnicodeEscapingJreTest.java
new file mode 100644
index 0000000..06a4d50
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/server/RequestFactoryUnicodeEscapingJreTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010 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.requestfactory.server;
+
+import com.google.gwt.requestfactory.client.RequestFactoryUnicodeEscapingTest;
+import com.google.gwt.requestfactory.shared.SimpleRequestFactory;
+
+/**
+ * A JRE implementation of {@link RequestFactoryUnicodeEscapingTest}.
+ */
+public class RequestFactoryUnicodeEscapingJreTest extends
+    RequestFactoryUnicodeEscapingTest {
+  @Override
+  public String getModuleName() {
+    return null;
+  }
+
+  @Override
+  protected SimpleRequestFactory createFactory() {
+    return RequestFactoryJreTest.createInProcess(SimpleRequestFactory.class);
+  }
+}
diff --git a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
index 4a1bfb7..1ac5987 100644
--- a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
+++ b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
@@ -19,11 +19,14 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.sql.Time;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -157,12 +160,72 @@
     return foo;
   }
 
+  /**
+   * Check client-side upcasting to BigDecimal and return a list of BigDecimals
+   * that should be upcast.
+   */
+  public static List<BigDecimal> processBigDecimalList(List<BigDecimal> values) {
+    List<BigDecimal> toReturn = new ArrayList<BigDecimal>();
+    toReturn.add(BigDecimal.TEN);
+    toReturn.add(new BigDecimal("12345.6789") {
+      // This is an anonymous subtype
+    });
+    if (!toReturn.equals(values)) {
+      throw new IllegalArgumentException(toReturn + " != " + values);
+    }
+    return toReturn;
+  }
+
+  /**
+   * Check client-side upcasting to BigInteger and return a list of BigIntegers
+   * that should be upcast.
+   */
+  public static List<BigInteger> processBigIntegerList(List<BigInteger> values) {
+    List<BigInteger> toReturn = new ArrayList<BigInteger>();
+    toReturn.add(BigInteger.TEN);
+    toReturn.add(new BigInteger("12345") {
+      // This is an anonymous subtype
+    });
+    if (!toReturn.equals(values)) {
+      throw new IllegalArgumentException(toReturn + " != " + values);
+    }
+    return toReturn;
+  }
+
   public static Boolean processBooleanList(List<Boolean> values) {
     return values.get(0);
   }
 
-  public static Date processDateList(List<Date> values) {
-    return values.get(0);
+  /**
+   * Check client-side upcasting to Date and return a list of Dates that should
+   * be upcast.
+   */
+  @SuppressWarnings("deprecation")
+  public static List<Date> processDateList(List<Date> values) {
+    // Keep these values in sync with SimpleFoo.processDateList
+    Date date = new Date(90, 0, 1);
+    java.sql.Date sqlDate = new java.sql.Date(90, 0, 2);
+    Time sqlTime = new Time(1, 2, 3);
+    Timestamp sqlTimestamp = new Timestamp(12345L);
+    List<Date> toReturn = Arrays.asList(date, sqlDate, sqlTime, sqlTimestamp);
+
+    if (toReturn.size() != values.size()) {
+      throw new IllegalArgumentException("size");
+    }
+
+    Iterator<Date> expected = toReturn.iterator();
+    Iterator<Date> actual = values.iterator();
+    while (expected.hasNext()) {
+      Date expectedDate = expected.next();
+      long expectedTime = expectedDate.getTime();
+      long actualTime = actual.next().getTime();
+      if (expectedTime != actualTime) {
+        throw new IllegalArgumentException(expectedDate.getClass().getName()
+            + " " + expectedTime + " != " + actualTime);
+      }
+    }
+
+    return toReturn;
   }
 
   public static SimpleEnum processEnumList(List<SimpleEnum> values) {
@@ -278,7 +341,7 @@
   public static String returnNullString() {
     return null;
   }
-  
+
   public static SimpleFoo returnSimpleFooSubclass() {
     return new SimpleFoo() {
     };
diff --git a/user/test/com/google/gwt/requestfactory/shared/BaseFooProxy.java b/user/test/com/google/gwt/requestfactory/shared/BaseFooProxy.java
index a628bf3..2e98883 100644
--- a/user/test/com/google/gwt/requestfactory/shared/BaseFooProxy.java
+++ b/user/test/com/google/gwt/requestfactory/shared/BaseFooProxy.java
@@ -97,6 +97,8 @@
 
   void setDoubleField(Double d);
 
+  void setEnumField(SimpleEnum value);
+
   void setFloatField(Float f);
 
   void setIntId(Integer intId);
diff --git a/user/test/com/google/gwt/requestfactory/shared/InstanceServiceRequest.java b/user/test/com/google/gwt/requestfactory/shared/InstanceServiceRequest.java
new file mode 100644
index 0000000..79fef75
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/shared/InstanceServiceRequest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 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.requestfactory.shared;
+
+import com.google.gwt.requestfactory.server.InstanceService;
+import com.google.gwt.requestfactory.server.InstanceServiceLocator;
+
+/**
+ * Used to test the ServiceLocator extension hook.
+ */
+@Service(value = InstanceService.class, locator = InstanceServiceLocator.class)
+public interface InstanceServiceRequest extends RequestContext {
+  Request<Integer> add(int value);
+}
diff --git a/user/test/com/google/gwt/requestfactory/shared/LocatorTest.java b/user/test/com/google/gwt/requestfactory/shared/LocatorTest.java
index fc550e8..095f65b 100644
--- a/user/test/com/google/gwt/requestfactory/shared/LocatorTest.java
+++ b/user/test/com/google/gwt/requestfactory/shared/LocatorTest.java
@@ -82,14 +82,13 @@
     static final Domain INSTANCE = new Domain();
   }
 
-  @ProxyFor(Domain.class)
-  @LocatorFor(DomainLocator.class)
+  @ProxyFor(value = Domain.class, locator = DomainLocator.class)
   interface DomainProxy extends EntityProxy {
     EntityProxyId<DomainProxy> stableId();
   };
 
   private static final String ID = "DomainId";
-  private static final int TEST_DELAY = 500;
+  private static final int TEST_DELAY = 5000;
 
   private Factory factory;
 
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java b/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
index cd174be..bbb4c6b 100644
--- a/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
+++ b/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.requestfactory.shared;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Date;
 import java.util.List;
 import java.util.Set;
@@ -61,9 +63,13 @@
 
   InstanceRequest<SimpleFooProxy, SimpleFooProxy> persistCascadingAndReturnSelf();
 
+  Request<List<BigInteger>> processBigIntegerList(List<BigInteger> values);
+
+  Request<List<BigDecimal>> processBigDecimalList(List<BigDecimal> values);
+
   Request<Boolean> processBooleanList(List<Boolean> values);
 
-  Request<Date> processDateList(List<Date> values);
+  Request<List<Date>> processDateList(List<Date> values);
 
   Request<SimpleEnum> processEnumList(List<SimpleEnum> values);
 
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java b/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
index 082f3a1..7def869 100644
--- a/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
+++ b/user/test/com/google/gwt/requestfactory/shared/SimpleRequestFactory.java
@@ -20,8 +20,12 @@
  * UserInformation and Logging services.
  */
 public interface SimpleRequestFactory extends BasicRequestFactory {
+  
+  InstanceServiceRequest instanceServiceRequest();
 
   SimpleBarRequest simpleBarRequest();
 
   SimpleFooRequest simpleFooRequest();
+
+  UnicodeTestRequest unicodeTestRequest();
 }
diff --git a/user/test/com/google/gwt/requestfactory/shared/UnicodeTestRequest.java b/user/test/com/google/gwt/requestfactory/shared/UnicodeTestRequest.java
new file mode 100644
index 0000000..fe9e38e
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/shared/UnicodeTestRequest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 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.requestfactory.shared;
+
+import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
+
+/**
+ * Provides access to the static test methods in {@link UnicodeEscapingTes}.
+ */
+@Service(UnicodeEscapingTest.class)
+public interface UnicodeTestRequest extends RequestContext {
+  Request<String> getStringContainingCharacterRange(int start, int end);
+
+  Request<Void> verifyStringContainingCharacterRange(int start, int end,
+      String str);
+}
diff --git a/user/test/com/google/gwt/text/TextSuite.java b/user/test/com/google/gwt/text/TextSuite.java
index cfe30d3..d14d55f 100644
--- a/user/test/com/google/gwt/text/TextSuite.java
+++ b/user/test/com/google/gwt/text/TextSuite.java
@@ -20,6 +20,7 @@
 import com.google.gwt.text.client.DoubleParserTest;
 import com.google.gwt.text.client.IntegerParserTest;
 import com.google.gwt.text.client.LongParserTest;
+import com.google.gwt.text.client.NumberFormatRendererTest;
 
 import junit.framework.Test;
 
@@ -34,6 +35,7 @@
     suite.addTestSuite(DoubleParserTest.class);
     suite.addTestSuite(IntegerParserTest.class);
     suite.addTestSuite(LongParserTest.class);
+    suite.addTestSuite(NumberFormatRendererTest.class);
     return suite;
   }
 }
diff --git a/user/test/com/google/gwt/text/client/DoubleParserTest.java b/user/test/com/google/gwt/text/client/DoubleParserTest.java
index d14414d..a2f8391 100644
--- a/user/test/com/google/gwt/text/client/DoubleParserTest.java
+++ b/user/test/com/google/gwt/text/client/DoubleParserTest.java
@@ -30,7 +30,7 @@
   }
   
   public void testOuroborus() throws ParseException {
-    assertEquals("123", DoubleRenderer.instance().render(DoubleParser.instance().parse("123")));
+    assertEquals("123.5", DoubleRenderer.instance().render(DoubleParser.instance().parse("123.5")));
   }
   
   public void testNull() throws ParseException {
diff --git a/user/test/com/google/gwt/text/client/NumberFormatRendererTest.java b/user/test/com/google/gwt/text/client/NumberFormatRendererTest.java
new file mode 100644
index 0000000..f57c016
--- /dev/null
+++ b/user/test/com/google/gwt/text/client/NumberFormatRendererTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 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.text.client;
+
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Eponymous unit test.
+ */
+public class NumberFormatRendererTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.text.TextSuite";
+  }
+
+  public void testDefault() {
+    assertEquals(NumberFormat.getDecimalFormat().format(Math.PI),
+        new NumberFormatRenderer().render(Math.PI));
+  }
+
+  public void testScientific() {
+    assertEquals(
+        NumberFormat.getScientificFormat().format(Math.PI),
+        new NumberFormatRenderer(NumberFormat.getScientificFormat()).render(Math.PI));
+  }
+
+  public void testNull() {
+    assertEquals("", new NumberFormatRenderer().render(null));
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
index bdceca6..5436f36 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -26,6 +26,7 @@
 import com.google.gwt.uibinder.attributeparsers.TextAlignConstantParserTest;
 import com.google.gwt.uibinder.attributeparsers.VerticalAlignmentConstantParserTest;
 import com.google.gwt.uibinder.elementparsers.AbsolutePanelParserTest;
+import com.google.gwt.uibinder.elementparsers.DateLabelParserTest;
 import com.google.gwt.uibinder.elementparsers.DialogBoxParserTest;
 import com.google.gwt.uibinder.elementparsers.DisclosurePanelParserTest;
 import com.google.gwt.uibinder.elementparsers.DockLayoutPanelParserTest;
@@ -36,6 +37,7 @@
 import com.google.gwt.uibinder.elementparsers.ListBoxParserTest;
 import com.google.gwt.uibinder.elementparsers.MenuBarParserTest;
 import com.google.gwt.uibinder.elementparsers.MenuItemParserTest;
+import com.google.gwt.uibinder.elementparsers.NumberLabelParserTest;
 import com.google.gwt.uibinder.elementparsers.StackLayoutPanelParserTest;
 import com.google.gwt.uibinder.elementparsers.StackPanelParserTest;
 import com.google.gwt.uibinder.elementparsers.TabLayoutPanelParserTest;
@@ -88,6 +90,7 @@
 
     // elementparsers
     suite.addTestSuite(AbsolutePanelParserTest.class);
+    suite.addTestSuite(DateLabelParserTest.class);
     suite.addTestSuite(DialogBoxParserTest.class);
     suite.addTestSuite(DisclosurePanelParserTest.class);
     suite.addTestSuite(DockLayoutPanelParserTest.class);
@@ -98,6 +101,7 @@
     suite.addTestSuite(ListBoxParserTest.class);
     suite.addTestSuite(MenuBarParserTest.class);
     suite.addTestSuite(MenuItemParserTest.class);
+    suite.addTestSuite(NumberLabelParserTest.class);
     suite.addTestSuite(StackLayoutPanelParserTest.class);
     suite.addTestSuite(StackPanelParserTest.class);
     suite.addTestSuite(TabLayoutPanelParserTest.class);
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/DateLabelParserTest.java b/user/test/com/google/gwt/uibinder/elementparsers/DateLabelParserTest.java
new file mode 100644
index 0000000..131f614
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/elementparsers/DateLabelParserTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2010 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.dev.javac.impl.MockJavaResource;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.TimeZone;
+import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.uibinder.rebind.FieldWriter;
+import com.google.gwt.user.client.ui.DateLabel;
+
+import junit.framework.TestCase;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Eponymous unit test.
+ */
+public class DateLabelParserTest extends TestCase {
+  private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.DateLabel";
+
+  private static final MockJavaResource DATELABEL_SUBCLASS_NO_CONSTRUCTOR = new MockJavaResource(
+      "my.MyDateLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package my;\n");
+      code.append("import com.google.gwt.user.client.ui.DateLabel;\n");
+      code.append("public class MyDateLabel extends DateLabel {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
+  private static final MockJavaResource DATELABEL_SUBCLASS_FORMAT_CONSTRUCTOR = new MockJavaResource(
+      "my.MyConstructedDateLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package my;\n");
+      code.append("import com.google.gwt.user.client.ui.DateLabel;\n");
+      code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n");
+      code.append("public class MyConstructedDateLabel extends DateLabel {\n");
+      code.append("  public MyConstructedDateLabel(DateTimeFormat f) { super(f); }");
+      code.append("}\n");
+      return code;
+    }
+  };
+  private static final MockJavaResource DATELABEL_SUBCLASS_FORMAT_AND_TZ_CONSTRUCTOR = new MockJavaResource(
+      "my.MyConstructedDateLabel2") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package my;\n");
+      code.append("import com.google.gwt.user.client.ui.DateLabel;\n");
+      code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n");
+      code.append("import com.google.gwt.i18n.client.TimeZone;\n");
+      code.append("public class MyConstructedDateLabel2 extends DateLabel {\n");
+      code.append("  public MyConstructedDateLabel2(DateTimeFormat f, TimeZone tz) { super(f, tz); }");
+      code.append("}\n");
+      return code;
+    }
+  };
+  private static final MockJavaResource DATELABEL_SUBCLASS_TZ_CONSTRUCTOR = new MockJavaResource(
+      "my.MyConstructedDateLabel3") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package my;\n");
+      code.append("import com.google.gwt.user.client.ui.DateLabel;\n");
+      code.append("import com.google.gwt.i18n.client.TimeZone;\n");
+      code.append("public class MyConstructedDateLabel3 extends DateLabel {\n");
+      code.append("  public MyConstructedDateLabel3(TimeZone tz) { super(); }");
+      code.append("}\n");
+      return code;
+    }
+  };
+  private ElementParserTester tester;
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    tester = new ElementParserTester(PARSED_TYPE, new DateLabelParser());
+  }
+
+  public void testHappyWithDefaultInstantiableSubclass()
+      throws UnableToCompleteException, SAXException {
+    tester = new ElementParserTester("my.MyDateLabel", new DateLabelParser(),
+        DATELABEL_SUBCLASS_NO_CONSTRUCTOR);
+    DateLabelParser parser = new DateLabelParser();
+    StringBuffer b = new StringBuffer();
+
+    b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'");
+    b.append("    xmlns:my='urn:import:my'");
+    b.append("    xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+    b.append("  <my:MyDateLabel format='{someDateTimeFormat}' timezone='{someTimeZone}' /> ");
+    b.append("</ui:UiBinder>");
+
+    parser.parse(tester.getElem(b.toString(), "my:MyDateLabel"), "fieldName",
+        tester.parsedType, tester.writer);
+    FieldWriter w = tester.fieldManager.lookup("fieldName");
+    assertNull(w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithSubclassWithDateTimeFormatConstructor()
+      throws UnableToCompleteException, SAXException {
+    DateLabelParser parser = new DateLabelParser();
+    tester = new ElementParserTester("my.MyConstructedDateLabel",
+        new DateLabelParser(), DATELABEL_SUBCLASS_FORMAT_CONSTRUCTOR);
+
+    StringBuffer b = new StringBuffer();
+
+    b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'");
+    b.append("    xmlns:my='urn:import:my'");
+    b.append("    xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+    b.append("  <my:MyConstructedDateLabel format='{someDateTimeFormat}' timezone='{someTimeZone}' /> ");
+    b.append("</ui:UiBinder>");
+
+    parser.parse(tester.getElem(b.toString(), "my:MyConstructedDateLabel"),
+        "fieldName", tester.parsedType, tester.writer);
+    FieldWriter w = tester.fieldManager.lookup("fieldName");
+    assertEquals("new my.MyConstructedDateLabel(someDateTimeFormat)",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithSubclassWithDateTimeFormatAndTimeZoneConstructor()
+      throws UnableToCompleteException, SAXException {
+    DateLabelParser parser = new DateLabelParser();
+    tester = new ElementParserTester("my.MyConstructedDateLabel2",
+        new DateLabelParser(), DATELABEL_SUBCLASS_FORMAT_AND_TZ_CONSTRUCTOR);
+
+    StringBuffer b = new StringBuffer();
+
+    b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'");
+    b.append("    xmlns:my='urn:import:my'");
+    b.append("    xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+    b.append("  <my:MyConstructedDateLabel2 format='{someDateTimeFormat}' timezone='{someTimeZone}' /> ");
+    b.append("</ui:UiBinder>");
+
+    parser.parse(tester.getElem(b.toString(), "my:MyConstructedDateLabel2"),
+        "fieldName", tester.parsedType, tester.writer);
+    FieldWriter w = tester.fieldManager.lookup("fieldName");
+    assertEquals(
+        "new my.MyConstructedDateLabel2(someDateTimeFormat, someTimeZone)",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithSubclassWithTimeZoneFormatConstructor()
+      throws UnableToCompleteException, SAXException {
+    DateLabelParser parser = new DateLabelParser();
+    tester = new ElementParserTester("my.MyConstructedDateLabel3",
+        new DateLabelParser(), DATELABEL_SUBCLASS_TZ_CONSTRUCTOR);
+
+    StringBuffer b = new StringBuffer();
+
+    b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'");
+    b.append("    xmlns:my='urn:import:my'");
+    b.append("    xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+    b.append("  <my:MyConstructedDateLabel3 format='{someDateTimeFormat}' timezone='{someTimeZone}' /> ");
+    b.append("</ui:UiBinder>");
+
+    parser.parse(tester.getElem(b.toString(), "my:MyConstructedDateLabel3"),
+        "fieldName", tester.parsedType, tester.writer);
+    FieldWriter w = tester.fieldManager.lookup("fieldName");
+    assertNull(w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithNoFormat() throws UnableToCompleteException,
+      SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel>");
+    b.append("</g:DateLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertNull(w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithPredefinedFormat() throws UnableToCompleteException,
+      SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel predefinedFormat='DATE_MEDIUM'>");
+    b.append("</g:DateLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertEquals("new " + DateLabel.class.getCanonicalName() + "("
+        + DateTimeFormat.class.getCanonicalName() + ".getFormat("
+        + PredefinedFormat.class.getCanonicalName() + ".DATE_MEDIUM))",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithTimezoneOffset() throws UnableToCompleteException,
+      SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel customFormat='zzzz' timezoneOffset='-7200'>");
+    b.append("</g:DateLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertEquals("new " + DateLabel.class.getCanonicalName() + "("
+        + DateTimeFormat.class.getCanonicalName() + ".getFormat(\"zzzz\"), "
+        + TimeZone.class.getCanonicalName() + ".createTimeZone(-7200))",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testChokeOnNonDateTimeFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel format='someString' >");
+    b.append("</g:DateLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue("Expect to hear about DateTimeFormat",
+          tester.logger.died.contains("DateTimeFormat"));
+    }
+  }
+
+  public void testChokeOnNonTimeZone() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel format='{someDateTimeFormat}' timezone='someString' >");
+    b.append("</g:DateLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue("Expect to hear about TimeZone",
+          tester.logger.died.contains("TimeZone"));
+    }
+  }
+
+  public void testChokeOnUnknownPredefinedFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel predefinedFormat='someString' >");
+    b.append("</g:DateLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue("Expect to hear about PredefinedFormat",
+          tester.logger.died.contains("PredefinedFormat"));
+    }
+  }
+
+  public void testChokeOnMultipleFormats() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel predefinedFormat='DATE_FULL' customFormat='MM/dd'>");
+    b.append("</g:DateLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(DateLabelParser.AT_MOST_ONE_SPECIFIED_FORMAT));
+    }
+  }
+
+  public void testChokeOnMultipleTimeZones() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel format='{someDateTimeFormat}' timezone='{someTimeZone}' timezoneOffset='-7200'>");
+    b.append("</g:DateLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(DateLabelParser.AT_MOST_ONE_SPECIFIED_TIME_ZONE));
+    }
+  }
+
+  public void testChokeOnTimeZoneWithoutSpecifiedFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:DateLabel timezoneOffset='-7200'>");
+    b.append("</g:DateLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(DateLabelParser.NO_TIMEZONE_WITHOUT_SPECIFIED_FORMAT));
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/NumberLabelParserTest.java b/user/test/com/google/gwt/uibinder/elementparsers/NumberLabelParserTest.java
new file mode 100644
index 0000000..41fff15
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/elementparsers/NumberLabelParserTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2010 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.dev.javac.impl.MockJavaResource;
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.uibinder.rebind.FieldWriter;
+import com.google.gwt.user.client.ui.NumberLabel;
+
+import junit.framework.TestCase;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Eponymous unit test.
+ */
+public class NumberLabelParserTest extends TestCase {
+  private static final String PARSED_TYPE = "com.google.gwt.user.client.ui.NumberLabel";
+
+  private static final MockJavaResource NUMBERLABEL_SUBCLASS_NO_CONSTRUCTOR = new MockJavaResource(
+      "my.MyNumberLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package my;\n");
+      code.append("import com.google.gwt.user.client.ui.NumberLabel;\n");
+      code.append("public class MyNumberLabel extends NumberLabel {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
+  private static final MockJavaResource NUMBERLABEL_SUBCLASS_FORMAT_CONSTRUCTOR = new MockJavaResource(
+      "my.MyConstructedNumberLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package my;\n");
+      code.append("import com.google.gwt.user.client.ui.NumberLabel;\n");
+      code.append("import com.google.gwt.i18n.client.NumberFormat;\n");
+      code.append("public class MyConstructedNumberLabel extends NumberLabel {\n");
+      code.append("  public MyConstructedNumberLabel(NumberFormat f) { super(f); }");
+      code.append("}\n");
+      return code;
+    }
+  };
+  private ElementParserTester tester;
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    tester = new ElementParserTester(PARSED_TYPE, new NumberLabelParser());
+  }
+
+  public void testHappyWithDefaultInstantiableSubclass()
+      throws UnableToCompleteException, SAXException {
+    tester = new ElementParserTester("my.MyNumberLabel",
+        new NumberLabelParser(), NUMBERLABEL_SUBCLASS_NO_CONSTRUCTOR);
+    NumberLabelParser parser = new NumberLabelParser();
+    StringBuffer b = new StringBuffer();
+
+    b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'");
+    b.append("    xmlns:my='urn:import:my'");
+    b.append("    xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+    b.append("  <my:MyNumberLabel format='{someDateTimeFormat}' currencyData='{someCurrencyData}' /> ");
+    b.append("</ui:UiBinder>");
+
+    parser.parse(tester.getElem(b.toString(), "my:MyNumberLabel"), "fieldName",
+        tester.parsedType, tester.writer);
+    FieldWriter w = tester.fieldManager.lookup("fieldName");
+    assertNull(w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithSubclassWithNumberFormatConstructor()
+      throws UnableToCompleteException, SAXException {
+    NumberLabelParser parser = new NumberLabelParser();
+    tester = new ElementParserTester("my.MyConstructedNumberLabel",
+        new NumberLabelParser(), NUMBERLABEL_SUBCLASS_FORMAT_CONSTRUCTOR);
+
+    StringBuffer b = new StringBuffer();
+
+    b.append("<ui:UiBinder xmlns:ui='" + ElementParserTester.BINDER_URI + "'");
+    b.append("    xmlns:my='urn:import:my'");
+    b.append("    xmlns:g='urn:import:com.google.gwt.user.client.ui'>");
+    b.append("  <my:MyConstructedNumberLabel format='{someDateTimeFormat}' /> ");
+    b.append("</ui:UiBinder>");
+
+    parser.parse(tester.getElem(b.toString(), "my:MyConstructedNumberLabel"),
+        "fieldName", tester.parsedType, tester.writer);
+    FieldWriter w = tester.fieldManager.lookup("fieldName");
+    assertEquals("new my.MyConstructedNumberLabel(someDateTimeFormat)",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithNoFormat() throws UnableToCompleteException,
+      SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel>");
+    b.append("</g:NumberLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertNull(w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithCustomFormatAndCurrency()
+      throws UnableToCompleteException, SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel customFormat='#\u00A0\u00A4' currencyCode='EUR'>");
+    b.append("</g:NumberLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertEquals("new " + NumberLabel.class.getCanonicalName() + "("
+        + NumberFormat.class.getCanonicalName() + ".getFormat(\"#\u00A0\u00A4\", \"EUR\"))",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithCurrencyPredefinedFormat()
+      throws UnableToCompleteException, SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='CURRENCY'>");
+    b.append("</g:NumberLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertEquals("new " + NumberLabel.class.getCanonicalName() + "("
+        + NumberFormat.class.getCanonicalName() + ".getCurrencyFormat())",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testHappyWithCurrencyPredefinedFormatAndCurrencCode()
+      throws UnableToCompleteException, SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='CURRENCY' currencyCode='EUR'>");
+    b.append("</g:NumberLabel>");
+
+    FieldWriter w = tester.parse(b.toString());
+    assertEquals("new " + NumberLabel.class.getCanonicalName() + "("
+        + NumberFormat.class.getCanonicalName() + ".getCurrencyFormat(\"EUR\"))",
+        w.getInitializer());
+
+    assertTrue(tester.writer.statements.isEmpty());
+    assertNull(tester.logger.died);
+  }
+
+  public void testChokeOnNonDateTimeFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel format='someString' >");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue("Expect to hear about DateTimeFormat",
+          tester.logger.died.contains("DateTimeFormat"));
+    }
+  }
+
+  public void testChokeOnNonCurrencyData() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='CURRENCY' currencyData='someString' >");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue("Expect to hear about CurrencyData",
+          tester.logger.died.contains("CurrencyData"));
+    }
+  }
+
+  public void testChokeOnUnknownPredefinedFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='someString' >");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(tester.logger.died, tester.logger.died.contains(String.format(
+          NumberLabelParser.UNKNOWN_PREDEFINED_FORMAT, "someString")));
+    }
+  }
+
+  public void testChokeOnMultipleFormats() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='SCIENTIFIC' customFormat='#'>");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(NumberLabelParser.AT_MOST_ONE_SPECIFIED_FORMAT));
+    }
+  }
+
+  public void testChokeOnMultipleCurrencies() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='CURRENCY' currencyData='{someCurrencyData}' currencyCode='EUR'>");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(NumberLabelParser.AT_MOST_ONE_SPECIFIED_CURRENCY));
+    }
+  }
+
+  public void testChokeOnCurrencyWithoutSpecifiedFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel currencyCode='EUR'>");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(NumberLabelParser.NO_CURRENCY_WITHOUT_SPECIFIED_FORMAT));
+    }
+  }
+
+  public void testChokeOnCurrencyWithPredefinedFormat() throws SAXException {
+    StringBuffer b = new StringBuffer();
+    b.append("<g:NumberLabel predefinedFormat='PERCENT' currencyCode='EUR'>");
+    b.append("</g:NumberLabel>");
+
+    try {
+      tester.parse(b.toString());
+      fail();
+    } catch (UnableToCompleteException e) {
+      assertTrue(
+          tester.logger.died,
+          tester.logger.died.contains(NumberLabelParser.NO_CURRENCY_WITH_PREDEFINED_FORMAT));
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
index 91852ee..ad1280a 100644
--- a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
+++ b/user/test/com/google/gwt/uibinder/test/UiJavaResources.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
@@ -19,6 +19,7 @@
 import com.google.gwt.dev.javac.impl.MockJavaResource;
 import com.google.gwt.dev.resource.Resource;
 import com.google.gwt.dev.util.collect.HashSet;
+import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
 
 import java.util.Arrays;
 import java.util.Set;
@@ -95,6 +96,53 @@
       return code;
     }
   };
+  public static final MockJavaResource CURRENCY_DATA = new MockJavaResource(
+      "com.google.gwt.i18n.client.CurrencyData") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.i18n.client;\n");
+      code.append("public class CurrencyData {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
+  public static final MockJavaResource DATE_LABEL = new MockJavaResource(
+      "com.google.gwt.user.client.ui.DateLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.user.client.ui;\n");
+      code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n");
+      code.append("import com.google.gwt.i18n.client.TimeZone;\n");
+      code.append("public class DateLabel extends ValueLabel {\n");
+      code.append("  public DateLabel() { super(null); } ");
+      code.append("  public DateLabel(DateTimeFormat format) { super(null); } ");
+      code.append("  public DateLabel(DateTimeFormat format, TimeZone timeZone) { super(null); } ");
+      code.append("}\n");
+      return code;
+    }
+  };
+  public static final MockJavaResource DATE_TIME_FORMAT = new MockJavaResource(
+      "com.google.gwt.i18n.client.DateTimeFormat") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.i18n.client;\n");
+      code.append("public class DateTimeFormat {\n");
+      code.append("  public static enum PredefinedFormat {\n");
+      PredefinedFormat[] values = PredefinedFormat.values();
+      for (int i = 0; i < values.length; i++) {
+        code.append("    ").append(values[i].name());
+        if (i < values.length - 1) {
+          code.append(",\n");
+        }
+      }
+      code.append("  }\n");
+      code.append("}\n");
+      return code;
+    }
+  };
   public static final MockJavaResource DIALOG_BOX = new MockJavaResource(
       "com.google.gwt.user.client.ui.DialogBox") {
     @Override
@@ -352,6 +400,42 @@
       return code;
     }
   };
+  public static final MockJavaResource NUMBER_LABEL = new MockJavaResource(
+      "com.google.gwt.user.client.ui.NumberLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.user.client.ui;\n");
+      code.append("import com.google.gwt.i18n.client.NumberFormat;\n");
+      code.append("public class NumberLabel extends ValueLabel {\n");
+      code.append("  public NumberLabel() { super(null); } ");
+      code.append("  public NumberLabel(NumberFormat format) { super(null); } ");
+      code.append("}\n");
+      return code;
+    }
+  };
+  public static final MockJavaResource NUMBER_FORMAT = new MockJavaResource(
+      "com.google.gwt.i18n.client.NumberFormat") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.i18n.client;\n");
+      code.append("public class NumberFormat {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
+  public static final MockJavaResource RENDERER = new MockJavaResource(
+      "com.google.gwt.text.shared.Renderer") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.text.shared;\n");
+      code.append("public class Renderer<T> {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
   public static final MockJavaResource SPLIT_LAYOUT_PANEL = new MockJavaResource(
       "com.google.gwt.user.client.ui.SplitLayoutPanel") {
     @Override
@@ -432,6 +516,17 @@
       return code;
     }
   };
+  public static final MockJavaResource TIME_ZONE = new MockJavaResource(
+      "com.google.gwt.i18n.client.TimeZone") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.i18n.client;\n");
+      code.append("public class TimeZone {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
   public static final MockJavaResource UI_BINDER = new MockJavaResource(
       "com.google.gwt.uibinder.client.UiBinder") {
     @Override
@@ -467,6 +562,19 @@
       return code;
     }
   };
+  public static final MockJavaResource VALUE_LABEL = new MockJavaResource(
+      "com.google.gwt.user.client.ui.ValueLabel") {
+    @Override
+    protected CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.user.client.ui;\n");
+      code.append("import com.google.gwt.text.shared.Renderer;\n");
+      code.append("public class ValueLabel extends Widget {\n");
+      code.append("  public ValueLabel(Renderer renderer) {} ");
+      code.append("}\n");
+      return code;
+    }
+  };
   public static final MockJavaResource WIDGET = new MockJavaResource(
       "com.google.gwt.user.client.ui.Widget") {
     @Override
@@ -491,6 +599,9 @@
     rtn.add(CLICK_EVENT);
     rtn.add(CLICK_HANDLER);
     rtn.add(COMMAND);
+    rtn.add(CURRENCY_DATA);
+    rtn.add(DATE_LABEL);
+    rtn.add(DATE_TIME_FORMAT);
     rtn.add(DIALOG_BOX);
     rtn.add(DISCLOSURE_PANEL);
     rtn.add(DOCK_LAYOUT_PANEL);
@@ -512,6 +623,9 @@
     rtn.add(MENU_ITEM_SEPARATOR);
     rtn.add(MOUSE_OVER_EVENT);
     rtn.add(MOUSE_OVER_HANDLER);
+    rtn.add(NUMBER_LABEL);
+    rtn.add(NUMBER_FORMAT);
+    rtn.add(RENDERER);
     rtn.add(SPLIT_LAYOUT_PANEL);
     rtn.add(STACK_LAYOUT_PANEL);
     rtn.add(STACK_PANEL);
@@ -519,9 +633,11 @@
     rtn.add(TAB_LAYOUT_PANEL);
     rtn.add(TAB_PANEL);
     rtn.add(TEXT_BOX_BASE);
+    rtn.add(TIME_ZONE);
     rtn.add(UI_OBJECT);
     rtn.add(UI_BINDER);
     rtn.add(UI_FACTORY);
+    rtn.add(VALUE_LABEL);
     rtn.add(WIDGET);
     return rtn;
   }
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
index 7201ea0..74426a9 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -28,14 +28,17 @@
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.ui.AbsolutePanel;
 import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.DateLabel;
 import com.google.gwt.user.client.ui.DisclosurePanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HTMLPanel;
 import com.google.gwt.user.client.ui.Image;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.NamedFrame;
+import com.google.gwt.user.client.ui.NumberLabel;
 import com.google.gwt.user.client.ui.RadioButton;
 import com.google.gwt.user.client.ui.StackPanel;
+import com.google.gwt.user.client.ui.ValueLabel;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
@@ -469,6 +472,21 @@
     assertNotNull("NamedFrame exists", p);
   }
 
+  public void testDateLabel() {
+    DateLabel p = widgetUi.myDateLabel;
+    assertNotNull("DateLabel exists", p);
+  }
+
+  public void testNumberLabel() {
+    NumberLabel<Float> p = widgetUi.myNumberLabel;
+    assertNotNull("NumberLabel exists", p);
+  }
+
+  public void testValueLabel() {
+    ValueLabel<Double> p = widgetUi.myValueLabel;
+    assertNotNull("ValueLabel exists", p);
+  }
+
   public void testStringAttributeIgnoresStaticSetter() {
     // Assumes setPopupText() is overloaded such that there is a static
     // setPopupText(Foo, String) method.
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
index 766ce98..9cd127e 100644
--- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
+++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.java
@@ -27,12 +27,15 @@
 import com.google.gwt.resources.client.DataResource;
 import com.google.gwt.resources.client.ImageResource;
 import com.google.gwt.resources.client.CssResource.Shared;
+import com.google.gwt.text.client.DoubleRenderer;
+import com.google.gwt.text.shared.Renderer;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiFactory;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.AbsolutePanel;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.DateLabel;
 import com.google.gwt.user.client.ui.DisclosurePanel;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.HTML;
@@ -45,11 +48,13 @@
 import com.google.gwt.user.client.ui.MenuBar;
 import com.google.gwt.user.client.ui.MenuItem;
 import com.google.gwt.user.client.ui.NamedFrame;
+import com.google.gwt.user.client.ui.NumberLabel;
 import com.google.gwt.user.client.ui.PushButton;
 import com.google.gwt.user.client.ui.RadioButton;
 import com.google.gwt.user.client.ui.StackPanel;
 import com.google.gwt.user.client.ui.ToggleButton;
 import com.google.gwt.user.client.ui.Tree;
+import com.google.gwt.user.client.ui.ValueLabel;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
@@ -171,6 +176,11 @@
   @UiField Widget myAbsolutePanelItemB;
   @UiField Widget myAbsolutePanelItemC;
   @UiField NamedFrame myNamedFrame;
+  @UiField DateLabel myDateLabel;
+  @UiField NumberLabel<Float> myNumberLabel;
+  @UiField(provided = true) @SuppressWarnings("unchecked")
+  Renderer doubleRenderer = DoubleRenderer.instance();
+  @UiField ValueLabel<Double> myValueLabel;
 
   public WidgetBasedUi() {
     external.style().ensureInjected();
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
index b4d3710..08a6b83 100644
--- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
+++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
@@ -72,6 +72,10 @@
   for a resource to provide arbitrary objects to arbitrary attributes (look for FooLabel)
 </ui:with>
 
+<ui:with field='doubleRenderer' type='com.google.gwt.text.shared.Renderer'>
+  Used to test ValueLabel's @UiConstructor
+</ui:with>
+
 <ui:import field='com.google.gwt.uibinder.test.client.Constants.CONST_FOO'>
   Tests the static import of a single constant into the local namespace.
 </ui:import>
@@ -83,6 +87,7 @@
 <ui:import field='com.google.gwt.uibinder.test.client.Constants.MyEnum.*'>
   Tests the static import of an enum into the local namespace.
 </ui:import>
+
 <!--
   Tests creating a CssResource from an external file.
  -->
@@ -303,7 +308,8 @@
         <ui:attribute name="text" description="radio button name"/>
       </demo:PointlessRadioButtonSubclass>
 
-        <gwt:HorizontalPanel ui:field="myHorizontalPanel" horizontalAlignment="ALIGN_LEFT" verticalAlignment="ALIGN_MIDDLE">
+        <gwt:HorizontalPanel ui:field="myHorizontalPanel"
+            horizontalAlignment="ALIGN_LEFT" verticalAlignment="ALIGN_MIDDLE">
           <gwt:Cell><gwt:HTMLPanel>
       <p> ... a StackPanel ... </p>
 
@@ -640,6 +646,12 @@
 
   <gwt:NamedFrame ui:field='myNamedFrame' name='myName'/>
 
+  <gwt:DateLabel ui:field='myDateLabel' predefinedFormat='DATE_FULL' />
+
+  <gwt:NumberLabel ui:field='myNumberLabel' predefinedFormat='SCIENTIFIC' />
+
+  <gwt:ValueLabel ui:field='myValueLabel' renderer='{doubleRenderer}' />
+
    </gwt:HTMLPanel>
   </gwt:Dock>
 </gwt:DockPanel>
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index 200e004..02b6946 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -45,6 +45,7 @@
 import com.google.gwt.user.client.ui.DefaultSuggestionDisplayTest;
 import com.google.gwt.user.client.ui.DelegatingKeyboardListenerCollectionTest;
 import com.google.gwt.user.client.ui.DialogBoxTest;
+import com.google.gwt.user.client.ui.DirectionalTextHelperTest;
 import com.google.gwt.user.client.ui.DisclosurePanelTest;
 import com.google.gwt.user.client.ui.DockLayoutPanelRtlTest;
 import com.google.gwt.user.client.ui.DockLayoutPanelTest;
@@ -147,6 +148,7 @@
     suite.addTestSuite(DefaultSuggestionDisplayTest.class);
     suite.addTestSuite(DelegatingKeyboardListenerCollectionTest.class);
     suite.addTestSuite(DialogBoxTest.class);
+    suite.addTestSuite(DirectionalTextHelperTest.class);
     suite.addTestSuite(DisclosurePanelTest.class);
     suite.addTestSuite(DockLayoutPanelRtlTest.class);
     suite.addTestSuite(DockLayoutPanelTest.class);
@@ -220,4 +222,3 @@
     return suite;
   }
 }
-
diff --git a/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java b/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java
index 8dbc539..574c596 100644
--- a/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java
+++ b/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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,6 +15,7 @@
  */
 package com.google.gwt.user.cellview.client;
 
+import com.google.gwt.user.cellview.client.AbstractHasDataTestBase.IndexCell;
 import com.google.gwt.cell.client.Cell;
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.core.client.Scheduler;
@@ -56,8 +57,7 @@
     /**
      * The root data provider.
      */
-    private final ListDataProvider<String> rootDataProvider =
-        createDataProvider("");
+    private final ListDataProvider<String> rootDataProvider = createDataProvider("");
 
     public <T> NodeInfo<?> getNodeInfo(T value) {
       if (value == ROOT_VALUE) {
@@ -93,7 +93,7 @@
 
     /**
      * Create a data provider that extends the prefix by one letter.
-     *
+     * 
      * @param prefix the prefix string
      * @return a data provider
      */
@@ -163,7 +163,7 @@
 
   /**
    * Construct a new {@link AbstractCellTreeTestBase}.
-   *
+   * 
    * @param singlePathOnly true if the tree only supports a single open path
    */
   public AbstractCellTreeTestBase(boolean singlePathOnly) {
@@ -199,14 +199,16 @@
   public void testRenderWithKeyProvider() {
     // Create a cell that verifies the render args.
     final List<String> rendered = new ArrayList<String>();
-    final Cell<String> cell = new TextCell() {
+    final IndexCell<String> cell = new IndexCell<String>() {
       @Override
-      public void render(String data, Object key, SafeHtmlBuilder sb) {
-        int call = rendered.size(); 
+      public void render(Context context, String data, SafeHtmlBuilder sb) {
+        super.render(context, data, sb);
+        int call = rendered.size();
         rendered.add(data);
         assertTrue("render() called more than thrice", rendered.size() < 4);
 
         assertEquals(call + "value", data);
+        Object key = context.getKey();
         assertTrue(key instanceof Integer);
         assertEquals(call, key);
       }
@@ -241,11 +243,12 @@
       public void execute() {
         assertEquals("Cell#render() should be called exactly thrice", 3,
             rendered.size());
+        cell.assertLastRenderIndex(2);
         finishTest();
       }
     });
   }
-  
+
   /**
    * Test that opening a sibling node works.
    */
@@ -565,7 +568,7 @@
 
   /**
    * Create an {@link AbstractCellTree} to test.
-   *
+   * 
    * @param <T> the data type of the root value
    * @param model the {@link TreeViewModel} that backs the tree
    * @param rootValue the root value
@@ -588,7 +591,7 @@
 
   /**
    * Test the state of a {@link TreeNode}.
-   *
+   * 
    * @param node the node to test
    * @param parent the expected parent
    * @param index the expected index within the parent
diff --git a/user/test/com/google/gwt/user/cellview/client/AbstractHasDataTestBase.java b/user/test/com/google/gwt/user/cellview/client/AbstractHasDataTestBase.java
index e3427bf..d56a3f5 100644
--- a/user/test/com/google/gwt/user/cellview/client/AbstractHasDataTestBase.java
+++ b/user/test/com/google/gwt/user/cellview/client/AbstractHasDataTestBase.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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,9 +15,16 @@
  */
 package com.google.gwt.user.cellview.client;
 
+import com.google.gwt.cell.client.AbstractCell;
+import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.TextCell;
+import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.regexp.shared.MatchResult;
 import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.view.client.ListDataProvider;
 import com.google.gwt.view.client.Range;
@@ -30,13 +37,69 @@
  */
 public abstract class AbstractHasDataTestBase extends GWTTestCase {
 
+  /**
+   * A mock cell that tests the index specified in each method.
+   * 
+   * @param <C> the cell type
+   */
+  protected static class IndexCell<C> extends AbstractCell<C> {
+    private int lastBrowserEventIndex = -1;
+    private int lastEditingIndex = -1;
+    private int lastRenderIndex = -1;
+    private int lastResetFocusIndex = -1;
+
+    public IndexCell(String... consumedEvents) {
+      super(consumedEvents);
+    }
+
+    public void assertLastBrowserEventIndex(int expected) {
+      assertEquals(expected, lastBrowserEventIndex);
+    }
+
+    public void assertLastEditingIndex(int expected) {
+      assertEquals(expected, lastEditingIndex);
+    }
+
+    public void assertLastRenderIndex(int expected) {
+      assertEquals(expected, lastRenderIndex);
+    }
+
+    public void assertLastResetFocusIndex(int expected) {
+      assertEquals(expected, lastResetFocusIndex);
+    }
+
+    @Override
+    public boolean isEditing(Context context, Element parent, C value) {
+      this.lastEditingIndex = context.getIndex();
+      return false;
+    }
+
+    @Override
+    public void onBrowserEvent(Context context, Element parent, C value,
+        NativeEvent event, ValueUpdater<C> valueUpdater) {
+      this.lastBrowserEventIndex = context.getIndex();
+    }
+
+    @Override
+    public void render(Context context, C value, SafeHtmlBuilder sb) {
+      this.lastRenderIndex = context.getIndex();
+      sb.appendEscaped("index " + this.lastRenderIndex);
+    }
+
+    @Override
+    public boolean resetFocus(Context context, Element parent, C value) {
+      this.lastResetFocusIndex = context.getIndex();
+      return false;
+    }
+  }
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.user.cellview.CellView";
   }
 
   public void testGetVisibleItem() {
-    AbstractHasData<String> display = createAbstractHasData();
+    AbstractHasData<String> display = createAbstractHasData(new TextCell());
     ListDataProvider<String> provider = new ListDataProvider<String>(
         createData(0, 13));
     provider.addDataDisplay(display);
@@ -65,7 +128,7 @@
   }
 
   public void testGetVisibleItems() {
-    AbstractHasData<String> display = createAbstractHasData();
+    AbstractHasData<String> display = createAbstractHasData(new TextCell());
     ListDataProvider<String> provider = new ListDataProvider<String>();
     provider.addDataDisplay(display);
     display.setVisibleRange(10, 3);
@@ -82,8 +145,21 @@
     assertEquals("test 12", items.get(2));
   }
 
+  public void testResetFocus() {
+    IndexCell<String> cell = new IndexCell<String>();
+    AbstractHasData<String> display = createAbstractHasData(cell);
+    display.setRowData(createData(0, 10));
+    display.getPresenter().flush();
+
+    cell.assertLastResetFocusIndex(-1);
+    display.getPresenter().setKeyboardSelectedRow(5, false, false);
+    display.resetFocusOnCell();
+    cell.assertLastResetFocusIndex(5);
+  }
+
   public void testSetRowData() {
-    AbstractHasData<String> display = createAbstractHasData();
+    IndexCell<String> cell = new IndexCell<String>();
+    AbstractHasData<String> display = createAbstractHasData(cell);
 
     // Set exact data.
     List<String> values = createData(0, 62);
@@ -110,6 +186,13 @@
     assertTrue(display.isRowCountExact());
     assertEquals(values, display.getVisibleItems());
     assertEquals(new Range(0, 62), display.getVisibleRange());
+    display.getPresenter().flush();
+
+    // Render one row and verify the index.
+    display.setRowData(5, createData(100, 1));
+    display.getPresenter().flush();
+    assertEquals("test 100", display.getVisibleItem(5));
+    cell.assertLastRenderIndex(5);
   }
 
   public void testSetTabIndex() {
@@ -123,7 +206,7 @@
       }
     }
 
-    AbstractHasData<String> display = createAbstractHasData();
+    AbstractHasData<String> display = createAbstractHasData(new TextCell());
     ListDataProvider<String> provider = new ListDataProvider<String>(
         createData(0, 10));
     provider.addDataDisplay(display);
@@ -147,14 +230,16 @@
 
   /**
    * Create an {@link AbstractHasData} to test.
-   *
+   * 
+   * @param cell the cell to use
    * @return the widget to test
    */
-  protected abstract AbstractHasData<String> createAbstractHasData();
+  protected abstract AbstractHasData<String> createAbstractHasData(
+      Cell<String> cell);
 
   /**
    * Create a list of data for testing.
-   *
+   * 
    * @param start the start index
    * @param length the length
    * @return a list of data
diff --git a/user/test/com/google/gwt/user/cellview/client/CellListTest.java b/user/test/com/google/gwt/user/cellview/client/CellListTest.java
index de16617..cc42e22 100644
--- a/user/test/com/google/gwt/user/cellview/client/CellListTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/CellListTest.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 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
@@ -17,7 +17,11 @@
 
 import com.google.gwt.cell.client.Cell;
 import com.google.gwt.cell.client.TextCell;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.safehtml.shared.SafeHtml;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.view.client.ProvidesKey;
 
 import java.util.ArrayList;
@@ -29,13 +33,30 @@
 public class CellListTest extends AbstractHasDataTestBase {
 
   public void testGetRowElement() {
-    CellList<String> list = createAbstractHasData();
+    CellList<String> list = createAbstractHasData(new TextCell());
     list.setRowData(0, createData(0, 10));
 
     // Ensure that calling getRowElement() flushes all pending changes.
     assertNotNull(list.getRowElement(9));
   }
 
+  public void testCellEvent() {
+    IndexCell<String> cell = new IndexCell<String>("click");
+    CellList<String> list = createAbstractHasData(cell);
+    RootPanel.get().add(list);
+    list.setRowData(createData(0, 10));
+    list.getPresenter().flush();
+
+    // Trigger an event at index 5.
+    NativeEvent event = Document.get().createClickEvent(0, 0, 0, 0, 0, false,
+        false, false, false);
+    list.getRowElement(5).dispatchEvent(event);
+    cell.assertLastBrowserEventIndex(5);
+    cell.assertLastEditingIndex(5);
+
+    RootPanel.get().remove(list);
+  }
+
   /**
    * Test that the correct values are sent to the Cell to be rendered.
    */
@@ -44,12 +65,13 @@
     final List<String> rendered = new ArrayList<String>();
     final Cell<String> cell = new TextCell() {
       @Override
-      public void render(String data, Object key, SafeHtmlBuilder sb) {
+      public void render(Context context, SafeHtml data, SafeHtmlBuilder sb) {
         int call = rendered.size();
-        rendered.add(data);
+        rendered.add(data.asString());
         assertTrue("render() called more than ten times", rendered.size() < 11);
 
-        assertEquals("test " + call, data);
+        Object key = context.getKey();
+        assertEquals("test " + call, data.asString());
         assertTrue(key instanceof Integer);
         assertEquals(call, key);
       }
@@ -68,7 +90,7 @@
   }
 
   @Override
-  protected CellList<String> createAbstractHasData() {
-    return new CellList<String>(new TextCell());
+  protected CellList<String> createAbstractHasData(Cell<String> cell) {
+    return new CellList<String>(cell);
   }
 }
diff --git a/user/test/com/google/gwt/user/cellview/client/CellTableTest.java b/user/test/com/google/gwt/user/cellview/client/CellTableTest.java
index d47d9c4..535bcd8 100644
--- a/user/test/com/google/gwt/user/cellview/client/CellTableTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/CellTableTest.java
@@ -15,8 +15,11 @@
  */
 package com.google.gwt.user.cellview.client;
 
+import com.google.gwt.cell.client.Cell;
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.dom.client.TableCellElement;
 import com.google.gwt.dom.client.TableElement;
 import com.google.gwt.dom.client.TableRowElement;
@@ -26,6 +29,7 @@
 import com.google.gwt.user.cellview.client.CellTable.Style;
 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
 import com.google.gwt.user.client.ui.HasVerticalAlignment;
+import com.google.gwt.user.client.ui.RootPanel;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -64,7 +68,7 @@
   }
 
   public void testCellAlignment() {
-    CellTable<String> table = createAbstractHasData();
+    CellTable<String> table = createAbstractHasData(new TextCell());
     Column<String, String> column = new Column<String, String>(new TextCell()) {
       @Override
       public String getValue(String object) {
@@ -117,8 +121,25 @@
     assertTrue("bottom".equals(vAlign));
   }
 
+  public void testCellEvent() {
+    IndexCell<String> cell = new IndexCell<String>("click");
+    CellTable<String> table = createAbstractHasData(cell);
+    RootPanel.get().add(table);
+    table.setRowData(createData(0, 10));
+    table.getPresenter().flush();
+
+    // Trigger an event at index 5.
+    NativeEvent event = Document.get().createClickEvent(0, 0, 0, 0, 0, false,
+        false, false, false);
+    table.getRowElement(5).getCells().getItem(0).dispatchEvent(event);
+    cell.assertLastBrowserEventIndex(5);
+    cell.assertLastEditingIndex(5);
+
+    RootPanel.get().remove(table);
+  }
+
   public void testGetRowElement() {
-    CellTable<String> table = createAbstractHasData();
+    CellTable<String> table = createAbstractHasData(new TextCell());
     table.setRowData(0, createData(0, 10));
 
     // Ensure that calling getRowElement() flushes all pending changes.
@@ -238,9 +259,9 @@
   }
 
   @Override
-  protected CellTable<String> createAbstractHasData() {
+  protected CellTable<String> createAbstractHasData(Cell<String> cell) {
     CellTable<String> table = new CellTable<String>();
-    table.addColumn(new Column<String, String>(new TextCell()) {
+    table.addColumn(new Column<String, String>(cell) {
       @Override
       public String getValue(String object) {
         return object;
diff --git a/user/test/com/google/gwt/user/cellview/client/ColumnTest.java b/user/test/com/google/gwt/user/cellview/client/ColumnTest.java
index c96a0a7..6190c90 100644
--- a/user/test/com/google/gwt/user/cellview/client/ColumnTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/ColumnTest.java
@@ -19,6 +19,7 @@
 import com.google.gwt.cell.client.FieldUpdater;
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -38,12 +39,12 @@
       AbstractEditableCell<String, String> {
 
     @Override
-    public boolean isEditing(Element parent, String value, Object key) {
+    public boolean isEditing(Context context, Element parent, String value) {
       return false;
     }
 
     @Override
-    public void render(String value, Object key, SafeHtmlBuilder sb) {
+    public void render(Context context, String value, SafeHtmlBuilder sb) {
       sb.appendEscaped(value);
     }
   }
@@ -101,7 +102,7 @@
         false, false, false, false);
     final MockEditableCell cell = new MockEditableCell() {
       @Override
-      public void onBrowserEvent(Element parent, String value, Object key,
+      public void onBrowserEvent(Context context, Element parent, String value,
           NativeEvent event, final ValueUpdater<String> valueUpdater) {
         setViewData("test", "newViewData");
         new Timer() {
@@ -126,7 +127,8 @@
     // Fire the event to the cell.
     delayTestFinish(5000);
     cell.setViewData("test", "oldViewData");
-    column.onBrowserEvent(theElem, 3, "test", theEvent, null);
+    Context context = new Context(3, 0, null);
+    column.onBrowserEvent(context, theElem, "test", theEvent);
   }
 
   public void testGetCell() {
@@ -141,7 +143,7 @@
         false, false, false, false);
     final MockEditableCell cell = new MockEditableCell() {
       @Override
-      public void onBrowserEvent(Element parent, String value, Object key,
+      public void onBrowserEvent(Context context, Element parent, String value,
           NativeEvent event, ValueUpdater<String> valueUpdater) {
         assertEquals(theElem, parent);
         assertEquals("test", value);
@@ -164,7 +166,8 @@
     column.setFieldUpdater(fieldUpdater);
 
     cell.setViewData("test", "oldViewData");
-    column.onBrowserEvent(theElem, 3, "test", theEvent, null);
+    Context context = new Context(3, 0, null);
+    column.onBrowserEvent(context, theElem, "test", theEvent);
 
     fieldUpdater.assertUpdateCalled(true);
     fieldUpdater.assertIndex(3);
@@ -178,7 +181,7 @@
         false, false, false, false);
     final MockEditableCell cell = new MockEditableCell() {
       @Override
-      public void onBrowserEvent(Element parent, String value, Object key,
+      public void onBrowserEvent(Context context, Element parent, String value,
           NativeEvent event, ValueUpdater<String> valueUpdater) {
         assertEquals(theElem, parent);
         assertEquals("test", value);
@@ -191,7 +194,8 @@
     Column<String, String> column = new IdentityColumn<String>(cell);
 
     cell.setViewData("test", "oldViewData");
-    column.onBrowserEvent(theElem, 3, "test", theEvent, null);
+    Context context = new Context(3, 0, null);
+    column.onBrowserEvent(context, theElem, "test", theEvent);
   }
 
   public void testRender() {
@@ -199,7 +203,8 @@
     Column<String, String> column = new IdentityColumn<String>(cell);
 
     SafeHtmlBuilder sb = new SafeHtmlBuilder();
-    column.render("test", null, sb);
+    Context context = new Context(0, 0, null);
+    column.render(context, "test", sb);
     assertEquals("test", sb.toSafeHtml().asString());
   }
 }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
index 1303260..0a8c7fa 100644
--- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
@@ -108,7 +108,7 @@
   }
 
   /** start of current block being tested. */
-  private int current;
+  protected int current;
 
   @Override
   public String getModuleName() {
@@ -234,7 +234,7 @@
         NON_BMP_TEST_INCREMENT);
   }
 
-  private void clientToServerVerifyRange(final int start, final int end,
+  protected void clientToServerVerifyRange(final int start, final int end,
       final int size, final int step) throws InvalidCharacterException {
     current = start;
     int blockEnd = Math.min(end, current + size);
@@ -274,7 +274,7 @@
         });
   }
 
-  private void serverToClientVerify(final int start, final int end,
+  protected void serverToClientVerify(final int start, final int end,
       final int size, final int step) {
     current = start;
     getService().getStringContainingCharacterRange(start,
diff --git a/user/test/com/google/gwt/user/client/ui/DirectionalTextHelperTest.java b/user/test/com/google/gwt/user/client/ui/DirectionalTextHelperTest.java
new file mode 100644
index 0000000..a5e9561
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DirectionalTextHelperTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2010 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.user.client.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.BidiUtils;
+import com.google.gwt.i18n.client.HasDirection.Direction;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.i18n.shared.AnyRtlDirectionEstimator;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests {@link DirectionalTextHelper}.
+ */
+public class DirectionalTextHelperTest extends GWTTestCase {
+
+  private final String EN_TEXT = "abc";
+  private final String IW_TEXT = "\u05e0\u05e1\u05e2";
+  private final String EN_HTML = "<b style=\"color: red\">" + EN_TEXT + "</b>";
+  private final String IW_HTML = "<b style=\"color: red\">" + IW_TEXT + "</b>";
+  private Element element;
+  private DirectionalTextHelper directionalTextHelper;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  // setDirection is deprecated; this only assures backwards compatibility.
+  public void testSetDirection() {
+    element = Document.get().createSpanElement();
+    directionalTextHelper = new DirectionalTextHelper(element,
+        /* is inline? */ true);
+
+    directionalTextHelper.setDirection(Direction.RTL);
+    assertDirection("element's direction is incorrect after setDirection",
+        Direction.RTL);
+
+    directionalTextHelper.setTextOrHtml(EN_TEXT, Direction.LTR, false);
+    assertDirection("target's direction is incorrect after setText with a" +
+        "specific direction", Direction.LTR);
+
+    directionalTextHelper.setTextOrHtml(EN_TEXT, false);
+    assertDirection("target's direction wasn't reverted to the direction set" +
+        "by last setDirection when calling setText with no direction argument" +
+        "and without a directionEstimator", Direction.RTL);
+
+    // We also specifically assert that the direction of the topmost element
+    // matches the last setDirection. (this is needed only for inline elements).
+    assertEquals("element's direction does not match the direction set by " +
+        "last setDirection when calling setText with no direction argument " +
+        "and without a directionEstimator", Direction.RTL,
+        BidiUtils.getDirectionOnElement(element));
+  }
+
+  public void testSetDirectionEstimator() {
+    element = Document.get().createSpanElement();
+    BidiUtils.setDirectionOnElement(element, Direction.LTR);
+    directionalTextHelper = new DirectionalTextHelper(element,
+        /* is inline? */ true);
+    directionalTextHelper.setDirectionEstimator(true);
+    
+    // If the element is span-wrapped, a redundant refresh occurred.
+    assertFalse("setDirectionEstimator(true) refreshed appearance before text" +
+        "had been received", isSpanWrapped());
+    
+    directionalTextHelper.setDirectionEstimator(false);
+    directionalTextHelper.setTextOrHtml(IW_TEXT, false);
+    assertDirection("Original element's direction (LTR) was modified with no" +
+        "apparent reason", Direction.LTR);
+
+    directionalTextHelper.setDirectionEstimator(true);
+    assertDirection("Direction was not refreshed on " +
+        "setDirectionEstimator(true) after receiving text with no explicit " +
+        "direction", Direction.RTL);
+    
+    directionalTextHelper.setTextOrHtml(IW_TEXT, Direction.LTR, false);
+    directionalTextHelper.setDirectionEstimator(
+        AnyRtlDirectionEstimator.get());
+    assertDirection("Direction was refreshed on setDirectionEstimator after " +
+        "receiving text with explicit direction", Direction.LTR);
+    
+    directionalTextHelper.setTextOrHtml(IW_TEXT, false);
+    directionalTextHelper.setDirectionEstimator(false);
+    assertDirection("Direction was not reset to the initial element direction" +
+        " on turning off direction estimation when last call to setTextOrHtml" +
+        " did not declare explicit direction.", Direction.LTR);
+  }
+
+  public void testSetDirectionEstimatorAndSetHtml() {
+    testSetTextOrHtml(true);
+  }
+
+  public void testSetDirectionEstimatorAndSetText() {
+    testSetTextOrHtml(false);
+  }
+
+  /**
+   * Asserts that both the {@link HasDirectionalText#getTextDirection} and the
+   * physical dir attribute match the expected direction.
+   *
+   * @param message Assertion message
+   * @param expected Expected direction
+   */
+  private void assertDirection(String message, Direction expected) {
+    assertTrue("dir attribute mismatch: " + message,
+        expected == getElementDirection() ||
+        /* For inline elements, empty dir attribute is acceptable if LTR is
+         * expected and the locale is not RTL. */
+        isSpanWrapped() && getElementDirection() == Direction.DEFAULT &&
+        (expected == Direction.RTL) == LocaleInfo.getCurrentLocale().isRTL());
+
+    assertEquals("textDir mismatch: " + message, expected,
+        directionalTextHelper.getTextDirection());
+  }
+
+  private Direction getElementDirection() {
+    Element elem = isSpanWrapped() ? element.getFirstChildElement() : element;
+    return BidiUtils.getDirectionOnElement(elem);
+  }
+
+  // This will not work generally. It assumes that the widget's content isn't
+  // consist of a span tag.
+  private boolean isSpanWrapped() {
+    Element inner = element.getFirstChildElement();
+    return inner != null && inner.getTagName().equalsIgnoreCase("span");
+  }
+
+  private void testSetTextOrHtml(boolean isHtml) {
+    String enContent = isHtml ? EN_HTML : EN_TEXT;
+    String iwContent = isHtml ? IW_HTML : IW_TEXT;
+    for (int i = 0; i < 2; i++) {
+      boolean isDiv = i == 0;
+      String id = isDiv ? "div widget: " : "span widget: ";
+      element = isDiv ? Document.get().createDivElement() :
+          Document.get().createSpanElement();
+      directionalTextHelper = new DirectionalTextHelper(element,
+          /* is inline? */ !isDiv);
+
+      directionalTextHelper.setTextOrHtml(enContent, isHtml);
+      assertDirection(id + "widget's direction is not DEFAULT upon " +
+          "standard initialization", Direction.DEFAULT);
+
+      directionalTextHelper.setTextOrHtml(iwContent, Direction.RTL, isHtml);
+      assertDirection(id + "widget's direction is not RTL after it was" +
+          " explicitly set to RTL", Direction.RTL);
+
+      directionalTextHelper.setTextOrHtml(enContent, isHtml);
+      assertDirection(id + "widget's direction was not specified, and no" +
+          " estimator specified, thus should return to initial value (DEFAULT)",
+          Direction.DEFAULT);
+
+      // Toggling on direction estimation from now on.    
+      directionalTextHelper.setDirectionEstimator(true);
+      
+      assertDirection(id + "widget's direction wasn't instantly updated" +
+          " to LTR on switching direction estimation on", Direction.LTR);
+
+      directionalTextHelper.setTextOrHtml(iwContent, isHtml);
+      assertDirection(id + "widget's direction wasn't estimated as RTL",
+          Direction.RTL);
+
+      directionalTextHelper.setTextOrHtml(iwContent, Direction.LTR, isHtml);
+      assertDirection(id + "widget's direction is not LTR after it was" +
+          " explicitly set to LTR (direction estimation is on)", Direction.LTR);
+
+      directionalTextHelper.setTextOrHtml(iwContent, Direction.DEFAULT, isHtml);
+      assertDirection(id + "widget's direction is not DEFAULT after it" +
+          " was explicitly set to DEFAULT (direction estimation is on)",
+          Direction.DEFAULT);
+
+      // TODO(jlabanca): Need a cross-browser way to test innerHTML.
+      // assertEquals(id + "retreived html is incorrect", iwContent,
+      //     directionalTextHelper.getTextOrHtml(true).toLowerCase());
+      assertEquals(id + "retreived text is incorrect", IW_TEXT,
+          directionalTextHelper.getTextOrHtml(false).toLowerCase());
+    }
+  }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/HTMLTest.java b/user/test/com/google/gwt/user/client/ui/HTMLTest.java
index ee84fc1..a483950 100644
--- a/user/test/com/google/gwt/user/client/ui/HTMLTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HTMLTest.java
@@ -15,10 +15,7 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection.Direction;
-import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.safehtml.shared.SafeHtmlUtils;
 
 /**
@@ -28,9 +25,6 @@
 public class HTMLTest extends LabelTest {
 
   private static final String html = "<b>hello</b><i>world</i>";
-  private final String EN_HTML = "<b style=\"color: red\">" + EN_TEXT + "</b>";
-  private final String IW_HTML = "<b style=\"color: red\">" + IW_TEXT + "</b>";
-  private HTML label;
 
   @Override
   public String getModuleName() {
@@ -58,43 +52,6 @@
     assertEquals(Direction.RTL, htmlElementRTL.getTextDirection());
   }
 
-  // setDirection is deprecated; this only assures backwards compatibility.
-  public void testSetDirection() {
-    for (int i = 0; i < 2; i++) {
-      String id = i == 0 ? "div label: " : "span label: ";
-      label = HTML.wrap(i == 0 ? createAttachedDivElement() :
-          createAttachedSpanElement());
-      label.setDirection(Direction.RTL);
-      assertLabelDirection(id + "label's direction is incorrect after " +
-          "setDirection", Direction.RTL);
-
-      label.setText(EN_TEXT, Direction.LTR);
-      assertLabelDirection(id + "label's direction is incorrect after " +
-          "setText with a specific direction", Direction.LTR);
-
-      label.setText(EN_TEXT);
-      assertLabelDirection(id + "label's direction wasn't reverted to the " +
-          "direction set by last setDirection when calling setText with no " +
-          "direction argument and without a directionEstimator", Direction.RTL);
-      if (i == 1) {
-        // For span element, we also specifically assert that the direction of
-        // the topmost element matches the last setDirection.
-        assertEquals(id + "element's direction does not match the direction " +
-            "set by last setDirection when calling setText with no direction " +
-            "argument and without a directionEstimator", Direction.RTL,
-            BidiUtils.getDirectionOnElement(label.getElement()));
-      }
-    }
-  }
-
-  public void testSetDirectionEstimatorAndSetHtml() {
-    testSetDirectionEstimatorAndSetTextOrHtml(true);
-  }
-
-  public void testSetDirectionEstimatorAndSetText() {
-    testSetDirectionEstimatorAndSetTextOrHtml(false);
-  }
-
   public void testSetSafeHtml() {
     HTML htmlElement = new HTML("<b>foo</b>");
     htmlElement.setHTML(SafeHtmlUtils.fromSafeConstant(html));
@@ -111,98 +68,4 @@
     assertEquals(Direction.LTR, htmlElement.getDirection());
   }
 
-  /**
-   * Asserts that both the {@link Label#getTextDirection} and the physical dir
-   * attribute match the expected direction.
-   *
-   * @param message Assertion message
-   * @param expected Expected direction
-   */
-  private void assertLabelDirection(String message, Direction expected) {
-    assertTrue("attribute mismatch: " + message,
-        expected == getLabelDirection() ||
-        /* For inline elements, empty dir attribute is acceptable if LTR is
-         * expected and the locale is not RTL. */
-        isSpanWrapped() && getLabelDirection() == Direction.DEFAULT &&
-        expected == Direction.LTR && !LocaleInfo.getCurrentLocale().isRTL());
-
-    assertEquals("textDir mismatch: " + message, expected,
-        label.getTextDirection());
-  }
-
-  private Direction getLabelDirection() {
-    Element element = isSpanWrapped() ?
-        label.getElement().getFirstChildElement() : label.getElement();
-
-    return BidiUtils.getDirectionOnElement(element);
-  }
-
-  // This will not work generally. It assumes that the label's content isn't
-  // consist of a span tag.
-  private boolean isSpanWrapped() {
-    Element inner = label.getElement().getFirstChildElement();
-    return inner != null && inner.getTagName().equalsIgnoreCase("span");
-  }
-
-  private void setLabelTextOrHtml(String content, boolean isHtml) {
-    if (isHtml) {
-      label.setHTML(content);
-    } else {
-      label.setText(content);
-    }
-  }
-
-  private void setLabelTextOrHtml(String content, Direction dir, boolean isHtml) {
-    if (isHtml) {
-      label.setHTML(content, dir);
-    } else {
-      label.setText(content, dir);
-    }
-  }
-
-  private void testSetDirectionEstimatorAndSetTextOrHtml(boolean isHtml) {
-    String enContent = isHtml ? EN_HTML : EN_TEXT;
-    String iwContent = isHtml ? IW_HTML : IW_TEXT;
-    for (int i = 0; i < 2; i++) {
-      String id = i == 0 ? "div label: " : "span label: ";
-      label = HTML.wrap(i == 0 ? createAttachedDivElement() :
-          createAttachedSpanElement());
-
-      setLabelTextOrHtml(enContent, isHtml);
-      assertLabelDirection(id + "label's direction is not DEFAULT upon " +
-          "standard initialization", Direction.DEFAULT);
-
-      setLabelTextOrHtml(iwContent, Direction.RTL, isHtml);
-      assertLabelDirection(id + "label's direction is not RTL after it was" +
-          " explicitly set to RTL", Direction.RTL);
-
-      setLabelTextOrHtml(enContent, isHtml);
-      assertLabelDirection(id + "label's direction was not specified, and no" +
-          " estimator specified, thus should return to initial value (DEFAULT)",
-          Direction.DEFAULT);
-
-      label.setDirectionEstimator(true);
-      assertLabelDirection(id + "label's direction wasn't instantly updated" +
-          " to LTR on switching direction estimation on", Direction.LTR);
-
-      setLabelTextOrHtml(iwContent, isHtml);
-      assertLabelDirection(id + "label's direction wasn't estimated as RTL",
-          Direction.RTL);
-
-      setLabelTextOrHtml(iwContent, Direction.LTR, isHtml);
-      assertLabelDirection(id + "label's direction is not LTR after it was" +
-          " explicitly set to LTR (direction estimation is on)", Direction.LTR);
-
-      setLabelTextOrHtml(iwContent, Direction.DEFAULT, isHtml);
-      assertLabelDirection(id + "label's direction is not DEFAULT after it" +
-          " was explicitly set to DEFAULT (direction estimation is on)",
-          Direction.DEFAULT);
-
-      // TODO(jlabanca): Need a cross-browser way to test innerHTML.
-      // assertEquals(id + "retreived html is incorrect", iwContent,
-      //     label.getHTML().toLowerCase());
-      assertEquals(id + "retreived text is incorrect", IW_TEXT,
-          label.getText().toLowerCase());
-    }
-  }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/LabelTest.java b/user/test/com/google/gwt/user/client/ui/LabelTest.java
index 00a949d..d43d87e 100644
--- a/user/test/com/google/gwt/user/client/ui/LabelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/LabelTest.java
@@ -22,7 +22,6 @@
 import com.google.gwt.i18n.client.BidiUtils;
 import com.google.gwt.i18n.client.HasDirection.Direction;
 import com.google.gwt.junit.client.GWTTestCase;
-import com.google.gwt.safehtml.shared.SafeHtmlUtils;
 import com.google.gwt.user.client.ui.HasHorizontalAlignment.AutoHorizontalAlignmentConstant;
 import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
 
@@ -92,7 +91,7 @@
     // Initialize the div with a specific direction, to verify it remembers its
     // original direction on turning direction estimator off.
     BidiUtils.setDirectionOnElement(elem, Direction.LTR);
-    label = Label.wrap(createAttachedDivElement());
+    label = Label.wrap(elem);
 
     label.setAutoHorizontalAlignment(
         HasAutoHorizontalAlignment.ALIGN_CONTENT_END);
@@ -118,23 +117,28 @@
         HasAutoHorizontalAlignment.ALIGN_CONTENT_START);
 
     label.setDirectionEstimator(false);
-    assertAlign("direction was supposed to be reset to the original " +
-        "ALIGN_LEFT after turning off direction estimator, and automatic " +
-        "horizontal alignment was to ALIGN_CONTENT_START",
+    assertAlign("horizontal alignment was supposed to be reset to the " +
+        "original ALIGN_LEFT after turning off direction estimator, and " +
+        "automatic horizontal alignment was to ALIGN_CONTENT_START",
         HasHorizontalAlignment.ALIGN_LEFT,
         HasAutoHorizontalAlignment.ALIGN_CONTENT_START);
   }
 
-  @SuppressWarnings("deprecation")
-  public void testSetSafeHtml() {
-    Label label = new Label("foo");
-    label.setHTML(SafeHtmlUtils.fromSafeConstant(html1));
-    
-    assertEquals(html1, label.getTextOrHtml(true).toLowerCase());
-    
-    label.setHTML(SafeHtmlUtils.fromSafeConstant(html2), Direction.LTR);
-    
-    assertEquals(html2, label.getTextOrHtml(true).toLowerCase());
+  public void testSetDirection() {
+    Label label = new Label(createAttachedSpanElement());
+    label.setDirectionEstimator(true);
+    label.setText(IW_TEXT);
+
+    // Should be span wrapped.
+    assertTrue(label.getElement().getInnerHTML().toLowerCase().contains("span"));
+
+    // Should not be span wrapped.
+    label.setDirection(Direction.RTL);
+    assertEquals(Direction.RTL, label.getDirection());
+    assertFalse(label.getElement().getInnerHTML().toLowerCase().contains("span"));
+
+    // Should not be span wrapped.
+    label.setDirection(Direction.LTR);
     assertEquals(Direction.LTR, label.getDirection());
   }
 
diff --git a/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java b/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java
index 7ab159c..c1cec44 100644
--- a/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java
+++ b/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.view.client;
 
+import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.junit.client.GWTTestCase;
@@ -215,7 +216,7 @@
     NativeEvent nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0,
         false, false, false, false);
     CellPreviewEvent<String> event = new CellPreviewEvent<String>(nativeEvent,
-        display, 1, 0, "test 1", false, false);
+        display, new Context(1, 0, null), "test 1", false, false);
     manager.handleSelectionEvent(event, null, model);
     assertEquals("test 1", model.getSelectedObject());
 
@@ -226,16 +227,16 @@
     // Ctrl+Select the same value.
     nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0, true, false,
         false, true);
-    event = new CellPreviewEvent<String>(nativeEvent, display, 1, 0, "test 1",
-        false, false);
+    event = new CellPreviewEvent<String>(nativeEvent, display, new Context(1,
+        0, null), "test 1", false, false);
     manager.handleSelectionEvent(event, null, model);
     assertNull(model.getSelectedObject());
 
     // Spacebar a different value.
     nativeEvent = Document.get().createKeyUpEvent(false, false, false, false,
         32);
-    event = new CellPreviewEvent<String>(nativeEvent, display, 2, 0, "test 2",
-        false, false);
+    event = new CellPreviewEvent<String>(nativeEvent, display, new Context(2,
+        0, null), "test 2", false, false);
     manager.handleSelectionEvent(event, null, model);
     assertEquals("test 2", model.getSelectedObject());
 
@@ -253,13 +254,13 @@
     NativeEvent nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0,
         false, false, false, false);
     CellPreviewEvent<String> event = new CellPreviewEvent<String>(nativeEvent,
-        display, 1, 0, "test 1", false, false);
+        display, new Context(1, 0, null), "test 1", false, false);
     manager.handleSelectionEvent(event, SelectAction.DESELECT, model);
     assertEquals("test 0", model.getSelectedObject());
 
     // Deselect the same value.
-    event = new CellPreviewEvent<String>(nativeEvent, display, 0, 0, "test 0",
-        false, false);
+    event = new CellPreviewEvent<String>(nativeEvent, display, new Context(0,
+        0, null), "test 0", false, false);
     manager.handleSelectionEvent(event, SelectAction.DESELECT, model);
     assertNull(model.getSelectedObject());
   }
@@ -272,7 +273,7 @@
     NativeEvent nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0,
         false, false, false, false);
     CellPreviewEvent<String> event = new CellPreviewEvent<String>(nativeEvent,
-        display, 3, 0, "test 3", false, false);
+        display, new Context(3, 0, null), "test 3", false, false);
     manager.handleSelectionEvent(event, SelectAction.IGNORE, model);
     assertEquals("test 0", model.getSelectedObject());
   }
@@ -286,13 +287,13 @@
     NativeEvent nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0,
         false, false, false, false);
     CellPreviewEvent<String> event = new CellPreviewEvent<String>(nativeEvent,
-        display, 0, 0, "test 0", false, false);
+        display, new Context(0, 0, null), "test 0", false, false);
     manager.handleSelectionEvent(event, SelectAction.SELECT, model);
     assertEquals("test 0", model.getSelectedObject());
 
     // Select a different value.
-    event = new CellPreviewEvent<String>(nativeEvent, display, 1, 0, "test 1",
-        false, false);
+    event = new CellPreviewEvent<String>(nativeEvent, display, new Context(1,
+        0, null), "test 1", false, false);
     manager.handleSelectionEvent(event, SelectAction.SELECT, model);
     assertEquals("test 1", model.getSelectedObject());
   }
@@ -306,7 +307,7 @@
     NativeEvent nativeEvent = Document.get().createClickEvent(0, 0, 0, 0, 0,
         false, false, false, false);
     CellPreviewEvent<String> event = new CellPreviewEvent<String>(nativeEvent,
-        display, 1, 0, "test 1", false, false);
+        display, new Context(1, 0, null), "test 1", false, false);
     manager.handleSelectionEvent(event, SelectAction.TOGGLE, model);
     assertEquals("test 1", model.getSelectedObject());