Checkpoint patch on the way to making samples/expenses bookmarkable via
activities and places. Introduces some place classes (not yet used),
but mainly reorganizes the code:

* Refactors SlidingPanel out of ExpensesShell

* Refactors a Factory class to be in charge of instantiation. More DI
/ IOC, allows use of PlaceHistoryMapper, and maybe, just maybe, we
can get GIN going.

* Refactors an app class out of the entry point, making the former
trivial.

Next steps are extracting activities out of ExpensesList and
ExpensesDetail, and making the latter work from EntityProxyIds instead
of actual proxies.

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

Review by: robertvawter@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9091 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml
index 3b81103..e70fe2e 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/ExpensesCommon.gwt.xml
@@ -9,11 +9,20 @@
   <inherits name='com.google.gwt.user.cellview.CellView'/>
 
   <source path='client'/>
-  <source path='request'/>
-  <source path='place'/>
-  <source path='ui'/>
+  <source path='shared'/>
 
   <!-- Default Locale. -->
   <extend-property name="locale" values="en"/>
   <set-property-fallback name="locale" value="en"/>
+
+  <inherits name='com.google.gwt.logging.Logging'/>
+  <set-property name="gwt.logging.enabled" value="TRUE"/> 
+  <set-property name="gwt.logging.logLevel" value="SEVERE"/>
+  <set-property name="gwt.logging.consoleHandler" value="ENABLED" />
+  <set-property name="gwt.logging.developmentModeHandler" value="ENABLED" />
+  <set-property name="gwt.logging.firebugHandler" value="ENABLED" />
+  <set-property name="gwt.logging.hasWidgetsHandler" value="DISABLED" />
+  <set-property name="gwt.logging.popupHandler" value="DISABLED" />
+  <set-property name="gwt.logging.systemHandler" value="ENABLED" />
+  <set-property name="gwt.logging.simpleRemoteHandler" value="DISABLED" />
 </module>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Approval.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Approval.java
new file mode 100644
index 0000000..17a34f6
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Approval.java
@@ -0,0 +1,72 @@
+/*
+ * 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.expenses.client;
+
+import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.sample.expenses.client.style.Styles;
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+
+/**
+ * An enum describing the approval status.
+ */
+public enum Approval {
+  BLANK("", "inherit", Styles.resources().blankIcon()), APPROVED("Approved",
+      "#00aa00", Styles.resources().approvedIcon()), DENIED("Denied",
+      "#ff0000", Styles.resources().deniedIcon());
+
+  /**
+   * Get the {@link Approval} from the specified string.
+   * 
+   * @param approval the approval string
+   * @return the {@link Approval}
+   */
+  public static Approval from(String approval) {
+    if (APPROVED.is(approval)) {
+      return APPROVED;
+    } else if (DENIED.is(approval)) {
+      return DENIED;
+    }
+    return BLANK;
+  }
+
+  private final String color;
+  private final SafeHtml iconHtml;
+  private final String text;
+
+  private Approval(String text, String color, ImageResource res) {
+    this.text = text;
+    this.color = color;
+    this.iconHtml = SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(res).getHTML());
+  }
+
+  public String getColor() {
+    return color;
+  }
+
+  public SafeHtml getIconHtml() {
+    return iconHtml;
+  }
+
+  public String getText() {
+    return text;
+  }
+
+  public boolean is(String compare) {
+    return text.equals(compare);
+  }
+}
\ No newline at end of file
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java
index a37c2fc..eea8b71 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseDetails.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
@@ -43,18 +43,20 @@
 import com.google.gwt.requestfactory.shared.EntityProxyChange;
 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.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;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpenseProxy;
-import com.google.gwt.sample.expenses.client.request.ExpenseRequest;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
-import com.google.gwt.sample.expenses.client.request.ReportRequest;
 import com.google.gwt.sample.expenses.client.style.Styles;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseRequest;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
+import com.google.gwt.sample.expenses.shared.ReportRequest;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.cellview.client.CellTable;
@@ -124,8 +126,8 @@
   private class ApprovalCell extends
       AbstractInputCell<String, ApprovalViewData> {
 
-    private final String approvedText = Expenses.Approval.APPROVED.getText();
-    private final String deniedText = Expenses.Approval.DENIED.getText();
+    private final String approvedText = Approval.APPROVED.getText();
+    private final String deniedText = Approval.DENIED.getText();
     private final SafeHtml errorIconHtml;
     private final SafeHtml pendingIconHtml;
 
@@ -422,7 +424,7 @@
    */
   private final Label errorPopupMessage = new Label();
 
-  private ExpensesRequestFactory expensesRequestFactory;
+  private final ExpensesRequestFactory expensesRequestFactory;
 
   /**
    * The data provider that provides expense items.
@@ -462,12 +464,13 @@
    */
   private double totalApproved;
 
-  public ExpenseDetails() {
+  public ExpenseDetails(ExpensesRequestFactory expensesRequestFactory) {
+    this.expensesRequestFactory = expensesRequestFactory;
     createErrorPopup();
     initTable();
     initWidget(uiBinder.createAndBindUi(this));
     items = new ListDataProvider<ExpenseProxy>(
-        Expenses.EXPENSE_RECORD_KEY_PROVIDER);
+        new EntityProxyKeyProvider<ExpenseProxy>());
     items.addDataDisplay(table);
 
     // Switch to edit notes.
@@ -568,14 +571,9 @@
     }
   }
 
-  public void setExpensesRequestFactory(
-      ExpensesRequestFactory expensesRequestFactory) {
-    this.expensesRequestFactory = expensesRequestFactory;
-  }
-
   /**
    * Set the {@link ReportProxy} to show.
-   *
+   * 
    * @param report the {@link ReportProxy}
    * @param department the selected department
    * @param employee the selected employee
@@ -613,7 +611,7 @@
 
   /**
    * Add a column of a {@link Comparable} type using default comparators.
-   *
+   * 
    * @param <C> the column type
    * @param table the table
    * @param text the header text
@@ -631,7 +629,7 @@
 
   /**
    * Add a column with the specified comparators.
-   *
+   * 
    * @param <C> the column type
    * @param table the table
    * @param text the header text
@@ -681,7 +679,7 @@
 
   /**
    * Create a comparator for the column.
-   *
+   * 
    * @param <C> the column type
    * @param getter the {@link GetValue} used to get the cell value
    * @param descending true if descending, false if ascending
@@ -740,7 +738,7 @@
 
   /**
    * Return a formatted currency string.
-   *
+   * 
    * @param amount the amount in dollars
    * @return a formatted string
    */
@@ -770,7 +768,7 @@
    * Get the columns displayed in the expense table.
    */
   private String[] getExpenseColumns() {
-    return new String[]{
+    return new String[] {
         "amount", "approval", "category", "created", "description",
         "reasonDenied"};
   }
@@ -778,7 +776,7 @@
   private CellTable<ExpenseProxy> initTable() {
     CellTable.Resources resources = GWT.create(TableResources.class);
     table = new CellTable<ExpenseProxy>(100, resources,
-        Expenses.EXPENSE_RECORD_KEY_PROVIDER);
+        new EntityProxyKeyProvider<ExpenseProxy>());
     Styles.Common common = Styles.common();
 
     table.addColumnStyleName(0, common.spacerColumn());
@@ -896,7 +894,7 @@
     for (ExpenseProxy record : records) {
       double cost = record.getAmount();
       totalCost += cost;
-      if (Expenses.Approval.APPROVED.is(record.getApproval())) {
+      if (Approval.APPROVED.is(record.getApproval())) {
         totalApproved += cost;
       }
     }
@@ -980,7 +978,7 @@
 
   /**
    * Set the state of the notes section.
-   *
+   * 
    * @param editable true for edit state, false for view state
    * @param pending true if changes are pending, false if not
    * @param notesText the current notes
@@ -999,7 +997,7 @@
 
   /**
    * Show the error popup.
-   *
+   * 
    * @param errorMessage the error message
    */
   private void showErrorPopup(String errorMessage) {
@@ -1015,7 +1013,7 @@
 
   /**
    * Update the state of a pending approval change.
-   *
+   * 
    * @param record the {@link ExpenseProxy} to sync
    * @param message the error message if rejected, or null if accepted
    */
@@ -1035,8 +1033,8 @@
   private void updateExpenseRecord(final ExpenseProxy record, String approval,
       String reasonDenied) {
     // Verify that the total is under the cap.
-    if (Expenses.Approval.APPROVED.is(approval)
-        && !Expenses.Approval.APPROVED.is(record.getApproval())) {
+    if (Approval.APPROVED.is(approval)
+        && !Approval.APPROVED.is(record.getApproval())) {
       double amount = record.getAmount();
       if (amount + totalApproved > MAX_COST) {
         syncCommit(record,
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java
index 99e7b00..c0f0c40 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseList.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
@@ -37,12 +37,14 @@
 import com.google.gwt.requestfactory.shared.EntityProxyChange;
 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.shared.SafeHtmlBuilder;
 import com.google.gwt.safehtml.shared.SafeHtmlUtils;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
 import com.google.gwt.sample.expenses.client.style.Styles;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiFactory;
 import com.google.gwt.uibinder.client.UiField;
@@ -85,7 +87,7 @@
 
     /**
      * Called when the user selects a report.
-     *
+     * 
      * @param report the selected report
      */
     void onReportSelected(ReportProxy report);
@@ -198,7 +200,7 @@
   /**
    * Utility method to get the first part of the breadcrumb based on the
    * department and employee.
-   *
+   * 
    * @param department the selected department
    * @param employee the selected employee
    * @return the breadcrumb
@@ -289,7 +291,8 @@
   /**
    * The data provider that provides reports.
    */
-  private final ReportDataProvider reports = new ReportDataProvider(Expenses.REPORT_RECORD_KEY_PROVIDER);
+  private final ReportDataProvider reports = new ReportDataProvider(
+      new EntityProxyKeyProvider<ReportProxy>());
 
   /**
    * The factory used to send requests.
@@ -363,7 +366,7 @@
 
   /**
    * Set the current department and employee to filter on.
-   *
+   * 
    * @param department the department, or null if none selected
    * @param employee the employee, or null if none selected
    */
@@ -395,7 +398,7 @@
 
   /**
    * Add a sortable column to the table.
-   *
+   * 
    * @param <C> the data type for the column
    * @param text the header text
    * @param cell the cell used to render the column
@@ -519,7 +522,7 @@
 
   /**
    * Send a request for reports in the current range.
-   *
+   * 
    * @param isPolling true if this request is caused by polling
    */
   private void requestReports(boolean isPolling) {
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 d7af1533..556a83b 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
@@ -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
@@ -22,12 +22,13 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Style.Overflow;
 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.shared.SafeHtml;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
 import com.google.gwt.sample.expenses.client.style.Styles;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
 import com.google.gwt.user.cellview.client.CellTree;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.view.client.AsyncDataProvider;
@@ -53,7 +54,7 @@
 
     /**
      * Called when the user selects a tree item.
-     *
+     * 
      * @param department the selected department name
      * @param employee the selected employee
      */
@@ -74,8 +75,7 @@
       super(Styles.resources().userIcon(), new AbstractCell<EmployeeProxy>() {
 
         private final String usernameStyle = Styles.common().usernameTreeItem();
-        private final String usernameStyleSelected =
-            Styles.common().usernameTreeItemSelected();
+        private final String usernameStyleSelected = Styles.common().usernameTreeItemSelected();
 
         @Override
         public boolean dependsOnSelection() {
@@ -83,8 +83,8 @@
         }
 
         @Override
-        public void render(
-            EmployeeProxy value, Object viewData, SafeHtmlBuilder sb) {
+        public void render(EmployeeProxy value, Object viewData,
+            SafeHtmlBuilder sb) {
           if (value != null) {
             StringBuilder classesBuilder = new StringBuilder(usernameStyle);
             if (lastEmployee != null
@@ -94,7 +94,8 @@
 
             sb.appendEscaped(value.getDisplayName());
             sb.appendHtmlConstant("<br>");
-            sb.append(template.span(classesBuilder.toString(), value.getUserName()));
+            sb.append(template.span(classesBuilder.toString(),
+                value.getUserName()));
           }
         }
       });
@@ -107,7 +108,8 @@
   /**
    * The {@link ListDataProvider} used for Employee lists.
    */
-  private class EmployeeListDataProvider extends AsyncDataProvider<EmployeeProxy> {
+  private class EmployeeListDataProvider extends
+      AsyncDataProvider<EmployeeProxy> {
 
     private final String department;
 
@@ -121,13 +123,13 @@
       super.addDataDisplay(display);
 
       // Request the count anytime a view is added.
-      requestFactory.employeeRequest().countEmployeesByDepartment(
-          department).fire(new Receiver<Long>() {
-        @Override
-        public void onSuccess(Long response) {
-          updateRowCount(response.intValue(), true);
-        }
-      });
+      requestFactory.employeeRequest().countEmployeesByDepartment(department).fire(
+          new Receiver<Long>() {
+            @Override
+            public void onSuccess(Long response) {
+              updateRowCount(response.intValue(), true);
+            }
+          });
     }
 
     @Override
@@ -135,11 +137,13 @@
       Range range = view.getVisibleRange();
       requestFactory.employeeRequest().findEmployeeEntriesByDepartment(
           department, range.getStart(), range.getLength()).with(
-          getEmployeeMenuProperties()).fire(new Receiver<List<EmployeeProxy>>(){
+          getEmployeeMenuProperties()).fire(
+          new Receiver<List<EmployeeProxy>>() {
             @Override
             public void onSuccess(List<EmployeeProxy> response) {
               updateRowData(0, response);
-            }});
+            }
+          });
     }
   }
 
@@ -161,8 +165,8 @@
     public <T> NodeInfo<?> getNodeInfo(T value) {
       if (value == null) {
         // Top level.
-        return new DefaultNodeInfo<String>(
-            departments, departmentCell, selectionModel, null);
+        return new DefaultNodeInfo<String>(departments, departmentCell,
+            selectionModel, null);
       } else if (isAllDepartment(value)) {
         // Employees are not displayed under the 'All' Department.
         return null;
@@ -170,8 +174,8 @@
         // Second level.
         EmployeeListDataProvider dataProvider = new EmployeeListDataProvider(
             (String) value);
-        return new DefaultNodeInfo<EmployeeProxy>(
-            dataProvider, employeeCell, selectionModel, null);
+        return new DefaultNodeInfo<EmployeeProxy>(dataProvider, employeeCell,
+            selectionModel, null);
       }
 
       return null;
@@ -223,16 +227,18 @@
   /**
    * The factory used to send requests.
    */
-  private ExpensesRequestFactory requestFactory;
+  private final ExpensesRequestFactory requestFactory;
 
   /**
    * The shared {@link SingleSelectionModel}.
    */
   private final SingleSelectionModel<Object> selectionModel = new SingleSelectionModel<Object>(
       new ProvidesKey<Object>() {
+        EntityProxyKeyProvider<EmployeeProxy> keyProvider = new EntityProxyKeyProvider<EmployeeProxy>();
+
         public Object getKey(Object item) {
           if (item instanceof EmployeeProxy) {
-            return Expenses.EMPLOYEE_RECORD_KEY_PROVIDER.getKey((EmployeeProxy) item);
+            return keyProvider.getKey((EmployeeProxy) item);
           }
           return item;
         }
@@ -243,11 +249,13 @@
    */
   private CellTree tree;
 
-  public ExpenseTree() {
+  public ExpenseTree(ExpensesRequestFactory requestFactory) {
+    this.requestFactory = requestFactory;
+    
     // Initialize the departments.
     List<String> departmentList = departments.getList();
     departmentList.add("All");
-    for (String department : Expenses.DEPARTMENTS) {
+    for (String department : ExpensesApp.DEPARTMENTS) {
       departmentList.add(department);
     }
 
@@ -261,10 +269,6 @@
     this.listener = listener;
   }
 
-  public void setRequestFactory(ExpensesRequestFactory factory) {
-    this.requestFactory = factory;
-  }
-
   /**
    * Create the {@link CellTree}.
    */
@@ -273,29 +277,28 @@
 
     // Listen for selection. We need to add this handler before the CellBrowser
     // adds its own handler.
-    selectionModel.addSelectionChangeHandler(
-        new SelectionChangeEvent.Handler() {
-          public void onSelectionChange(SelectionChangeEvent event) {
-            Object selected = selectionModel.getSelectedObject();
-            if (selected == null) {
-              lastEmployee = null;
-              lastDepartment = null;
-            } else if (selected instanceof EmployeeProxy) {
-              lastEmployee = (EmployeeProxy) selected;
-            } else if (selected instanceof String) {
-              lastEmployee = null;
-              if (model.isAllDepartment(selected)) {
-                lastDepartment = null;
-              } else {
-                lastDepartment = (String) selected;
-              }
-            }
-
-            if (listener != null) {
-              listener.onSelection(lastDepartment, lastEmployee);
-            }
+    selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
+      public void onSelectionChange(SelectionChangeEvent event) {
+        Object selected = selectionModel.getSelectedObject();
+        if (selected == null) {
+          lastEmployee = null;
+          lastDepartment = null;
+        } else if (selected instanceof EmployeeProxy) {
+          lastEmployee = (EmployeeProxy) selected;
+        } else if (selected instanceof String) {
+          lastEmployee = null;
+          if (model.isAllDepartment(selected)) {
+            lastDepartment = null;
+          } else {
+            lastDepartment = (String) selected;
           }
-        });
+        }
+
+        if (listener != null) {
+          listener.onSelection(lastDepartment, lastEmployee);
+        }
+      }
+    });
 
     // Create a CellBrowser.
     tree = new CellTree(model, null);
@@ -303,6 +306,6 @@
   }
 
   private String[] getEmployeeMenuProperties() {
-    return new String[]{"displayName", "userName"};
+    return new String[] {"displayName", "userName"};
   }
 }
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Expenses.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Expenses.java
index a7bc579..c4a66a7 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Expenses.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/Expenses.java
@@ -16,176 +16,15 @@
 package com.google.gwt.sample.expenses.client;
 
 import com.google.gwt.core.client.EntryPoint;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.event.shared.SimpleEventBus;
-import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.RequestEvent;
-import com.google.gwt.requestfactory.shared.UserInformationProxy;
-import com.google.gwt.requestfactory.ui.client.AuthenticationFailureHandler;
-import com.google.gwt.requestfactory.ui.client.LoginWidget;
-import com.google.gwt.resources.client.ImageResource;
-import com.google.gwt.safehtml.shared.SafeHtml;
-import com.google.gwt.safehtml.shared.SafeHtmlUtils;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpenseProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
-import com.google.gwt.sample.expenses.client.style.Styles;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.sample.expenses.client.ioc.Factory;
 import com.google.gwt.user.client.ui.RootLayoutPanel;
-import com.google.gwt.view.client.ProvidesKey;
 
 /**
  * Entry point for the Expenses app.
  */
 public class Expenses implements EntryPoint {
 
-  /**
-   * An enum describing the approval status.
-   */
-  public static enum Approval {
-    BLANK("", "inherit", Styles.resources().blankIcon()), APPROVED("Approved",
-        "#00aa00", Styles.resources().approvedIcon()), DENIED("Denied",
-        "#ff0000", Styles.resources().deniedIcon());
-
-    /**
-     * Get the {@link Approval} from the specified string.
-     * 
-     * @param approval the approval string
-     * @return the {@link Approval}
-     */
-    public static Approval from(String approval) {
-      if (APPROVED.is(approval)) {
-        return APPROVED;
-      } else if (DENIED.is(approval)) {
-        return DENIED;
-      }
-      return BLANK;
-    }
-
-    private final String color;
-    private final SafeHtml iconHtml;
-    private final String text;
-
-    private Approval(String text, String color, ImageResource res) {
-      this.text = text;
-      this.color = color;
-      this.iconHtml = SafeHtmlUtils.fromTrustedString(AbstractImagePrototype.create(res).getHTML());
-    }
-
-    public String getColor() {
-      return color;
-    }
-
-    public SafeHtml getIconHtml() {
-      return iconHtml;
-    }
-
-    public String getText() {
-      return text;
-    }
-
-    public boolean is(String compare) {
-      return text.equals(compare);
-    }
-  }
-
-  public static final String[] DEPARTMENTS = {
-      "Engineering", "Finance", "Marketing", "Operations", "Sales"};
-
-  /**
-   * The key provider for {@link EmployeeProxy}s.
-   */
-  public static final ProvidesKey<EmployeeProxy> EMPLOYEE_RECORD_KEY_PROVIDER =
-    new ProvidesKey<EmployeeProxy>() {
-    public Object getKey(EmployeeProxy item) {
-      return item == null ? null : item.getId();
-    }
-  };
-
-  /**
-   * The key provider for {@link ExpenseProxy}s.
-   */
-  public static final ProvidesKey<ExpenseProxy> EXPENSE_RECORD_KEY_PROVIDER =
-    new ProvidesKey<ExpenseProxy>() {
-    public Object getKey(ExpenseProxy item) {
-      return item == null ? null : item.getId();
-    }
-  };
-
-  /**
-   * The key provider for {@link ReportProxy}s.
-   */
-  public static final ProvidesKey<ReportProxy> REPORT_RECORD_KEY_PROVIDER =
-    new ProvidesKey<ReportProxy>() {
-    public Object getKey(ReportProxy item) {
-      return (item == null) ? null : item.getId();
-    }
-  };
-
-  private String lastDepartment;
-  private EmployeeProxy lastEmployee;
-  private ExpensesRequestFactory requestFactory;
-  private ExpensesShell shell;
-
   public void onModuleLoad() {
-    GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
-      public void onUncaughtException(Throwable e) {
-        Window.alert("Error: " + e.getMessage());
-//        placeController.goTo(Place.NOWHERE);
-      }
-    });
-    
-    final EventBus eventBus = new SimpleEventBus();
-    requestFactory = GWT.create(ExpensesRequestFactory.class);
-    requestFactory.initialize(eventBus);
-
-    RootLayoutPanel root = RootLayoutPanel.get();
-
-    shell = new ExpensesShell();
-    final ExpenseTree expenseTree = shell.getExpenseTree();
-    final ExpenseList expenseList = shell.getExpenseList();
-    final ExpenseDetails expenseDetails = shell.getExpenseDetails();
-
-    root.add(shell);
-    
-    // Check for Authentication failures or mismatches
-    RequestEvent.register(eventBus, new AuthenticationFailureHandler());
-
-    // Add a login widget to the page
-    final LoginWidget login = shell.getLoginWidget();
-    Receiver<UserInformationProxy> receiver = new Receiver<UserInformationProxy>() {
-      @Override
-      public void onSuccess(UserInformationProxy userInformationRecord) {
-        login.setUserInformation(userInformationRecord);
-      }       
-     };
-     requestFactory.userInformationRequest().getCurrentUserInformation(
-         Location.getHref()).fire(receiver);
-
-    // Listen for requests from ExpenseTree.
-    expenseTree.setListener(new ExpenseTree.Listener() {
-      public void onSelection(String department, EmployeeProxy employee) {
-        lastDepartment = department;
-        lastEmployee = employee;
-        expenseList.setEmployee(department, employee);
-        shell.showExpenseDetails(false);
-      }
-    });
-    expenseTree.setRequestFactory(requestFactory);
-
-    // Listen for requests from the ExpenseList.
-    expenseList.setListener(new ExpenseList.Listener() {
-      public void onReportSelected(ReportProxy report) {
-        expenseDetails.setExpensesRequestFactory(requestFactory);
-        expenseDetails.setReportRecord(report, lastDepartment, lastEmployee);
-        shell.showExpenseDetails(true);
-      }
-    });
-    expenseList.init(requestFactory, eventBus);
-    expenseDetails.init(eventBus);
+    new Factory().getExpensesApp().run(RootLayoutPanel.get());
   }
 }
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java
new file mode 100644
index 0000000..63625d0
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesApp.java
@@ -0,0 +1,128 @@
+/*
+ * 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.expenses.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.place.shared.Place;
+import com.google.gwt.place.shared.PlaceController;
+import com.google.gwt.place.shared.PlaceHistoryHandler;
+import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.requestfactory.shared.RequestEvent;
+import com.google.gwt.requestfactory.shared.UserInformationProxy;
+import com.google.gwt.requestfactory.ui.client.AuthenticationFailureHandler;
+import com.google.gwt.requestfactory.ui.client.LoginWidget;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
+import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.user.client.ui.HasWidgets;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Entry point for the Expenses app.
+ */
+public class ExpensesApp {
+  /**
+   * TODO: This belongs on the server, probably as an entity
+   */
+  public static final String[] DEPARTMENTS = {
+      "Engineering", "Finance", "Marketing", "Operations", "Sales"};
+
+  private static final Logger log = Logger.getLogger(ExpensesShell.class.getName());
+
+  private final EventBus eventBus;
+  private final PlaceController placeController;
+  private final PlaceHistoryHandler placeHistoryHandler;
+  private final ExpensesRequestFactory requestFactory;
+  private final ExpensesShell shell;
+
+  private String lastDepartment;
+  private EmployeeProxy lastEmployee;
+
+  public ExpensesApp(ExpensesRequestFactory requestFactory, EventBus eventBus,
+      ExpensesShell shell, PlaceHistoryHandler placeHistoryHandler,
+      PlaceController placeController) {
+    this.requestFactory = requestFactory;
+    this.eventBus = eventBus;
+    this.shell = shell;
+    this.placeHistoryHandler = placeHistoryHandler;
+    this.placeController = placeController;
+  }
+
+  /**
+   * Start the app, and add its main widget to the given panel.
+   */
+  public void run(HasWidgets root) {
+    GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
+      public void onUncaughtException(Throwable e) {
+        log.log(Level.SEVERE, e.getMessage(), e);
+      }
+    });
+
+    final ExpenseTree expenseTree = shell.getExpenseTree();
+    final ExpenseList expenseList = shell.getExpenseList();
+    final ExpenseDetails expenseDetails = shell.getExpenseDetails();
+
+    root.add(shell);
+
+    // Check for Authentication failures or mismatches
+    RequestEvent.register(eventBus, new AuthenticationFailureHandler());
+
+    // Kick off the login widget
+    final LoginWidget login = shell.getLoginWidget();
+    Receiver<UserInformationProxy> receiver = new Receiver<UserInformationProxy>() {
+      @Override
+      public void onSuccess(UserInformationProxy userInformationRecord) {
+        login.setUserInformation(userInformationRecord);
+      }
+    };
+    requestFactory.userInformationRequest().getCurrentUserInformation(
+        Location.getHref()).fire(receiver);
+
+    // Listen for requests from ExpenseTree.
+    expenseTree.setListener(new ExpenseTree.Listener() {
+      public void onSelection(String department, EmployeeProxy employee) {
+        lastDepartment = department;
+        lastEmployee = employee;
+        expenseList.setEmployee(department, employee);
+        shell.showExpenseDetails(false);
+      }
+    });
+
+    // Listen for requests from the ExpenseList.
+    expenseList.setListener(new ExpenseList.Listener() {
+      public void onReportSelected(ReportProxy report) {
+        expenseDetails.setReportRecord(report, lastDepartment, lastEmployee);
+        shell.showExpenseDetails(true);
+      }
+    });
+
+    /*
+     * TODO these should be constructor arguments, and the inits should probably
+     * happen onLoad
+     */
+    expenseList.init(requestFactory, eventBus);
+    expenseDetails.init(eventBus);
+
+    // Browser history integration
+    placeHistoryHandler.register(placeController, eventBus, new Place() {
+    });
+    placeHistoryHandler.handleCurrentHistory();
+  }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java
index 24ec2f4..e7ef3f4 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobile.java
@@ -24,8 +24,8 @@
 import com.google.gwt.requestfactory.shared.UserInformationProxy;
 import com.google.gwt.requestfactory.ui.client.AuthenticationFailureHandler;
 import com.google.gwt.requestfactory.ui.client.LoginWidget;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.Window.Location;
 import com.google.gwt.user.client.ui.Label;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java
index f436c50..f35cf1d 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesMobileShell.java
@@ -20,10 +20,10 @@
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.requestfactory.ui.client.LoginWidget;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpenseProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.uibinder.client.UiHandler;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java
index 9d80fa8..0e79d0f 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.java
@@ -16,24 +16,19 @@
 package com.google.gwt.sample.expenses.client;
 
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.layout.client.Layout;
-import com.google.gwt.layout.client.Layout.Layer;
 import com.google.gwt.requestfactory.ui.client.LoginWidget;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.DockLayoutPanel;
-import com.google.gwt.user.client.ui.LayoutPanel;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
  * UI shell for expenses sample app.
  */
 public class ExpensesShell extends Composite {
-
   interface ShellUiBinder extends UiBinder<Widget, ExpensesShell> {
   }
 
@@ -41,18 +36,20 @@
 
   @UiField
   ExpenseList expenseList;
+  @UiField(provided = true)
+  final ExpenseTree expenseTree;
   @UiField
-  ExpenseTree expenseTree;
-  @UiField
-  LayoutPanel layoutPanel;
+  SlidingPanel slidingPanel;
   @UiField
   LoginWidget loginWidget;
   @UiField
   DockLayoutPanel dockLayout;
+  @UiField(provided = true)
+  final ExpenseDetails expenseDetails;
 
-  private final ExpenseDetails expenseDetails = new ExpenseDetails();
-
-  public ExpensesShell() {
+  public ExpensesShell(ExpenseTree expenseTree, ExpenseDetails expenseDetails) {
+    this.expenseTree = expenseTree;
+    this.expenseDetails = expenseDetails;
     initWidget(uiBinder.createAndBindUi(this));
 
     // Handle breadcrumb events from Expense Details.
@@ -88,51 +85,6 @@
    * @param isShowing true to show details, false to show reports list
    */
   public void showExpenseDetails(boolean isShowing) {
-    if (isShowing) {
-      showWidget(expenseDetails, false);
-    } else {
-      showWidget(expenseList, true);
-    }
-  }
-
-  /**
-   * Slide a widget into view.
-   * 
-   * @param widget the widget to show
-   * @param fromLeft true to slide from left, false to slide from right
-   */
-  private void showWidget(Widget widget, boolean fromLeft) {
-    // Early out if the widget is already in the layout panel.
-    final Widget current = layoutPanel.getWidget(0);
-    if (current == widget) {
-      return;
-    }
-
-    // Initialize the layout.
-    layoutPanel.add(widget);
-    layoutPanel.setWidgetLeftWidth(current, 0, Unit.PCT, 100, Unit.PCT);
-    if (fromLeft) {
-      layoutPanel.setWidgetLeftWidth(widget, -100, Unit.PCT, 100, Unit.PCT);
-    } else {
-      layoutPanel.setWidgetLeftWidth(widget, 100, Unit.PCT, 100, Unit.PCT);
-    }
-    layoutPanel.forceLayout();
-
-    // Slide into view.
-    if (fromLeft) {
-      layoutPanel.setWidgetLeftWidth(current, 100, Unit.PCT, 100, Unit.PCT);
-    } else {
-      layoutPanel.setWidgetLeftWidth(current, -100, Unit.PCT, 100, Unit.PCT);
-    }
-    layoutPanel.setWidgetLeftWidth(widget, 0, Unit.PCT, 100, Unit.PCT);
-    layoutPanel.animate(500, new Layout.AnimationCallback() {
-      public void onAnimationComplete() {
-        // Remove the old widget when the animation completes.
-        layoutPanel.remove(current);
-      }
-
-      public void onLayout(Layer layer, double progress) {
-      }
-    });
+    slidingPanel.setWidget(isShowing ? expenseDetails : expenseList);
   }
 }
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml
index 0ec4a00..bd54c12 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesShell.ui.xml
@@ -66,11 +66,11 @@
         </m:MobileScrollPanel>
       </g:west>
       <g:center>
-        <g:LayoutPanel ui:field='layoutPanel'>
-          <g:layer>
-            <e:ExpenseList ui:field='expenseList' />
-          </g:layer>
-        </g:LayoutPanel>
+        <e:SlidingPanel ui:field='slidingPanel'>
+          <!-- The order of the children determines which way they slide -->
+          <e:ExpenseList ui:field='expenseList' />
+          <e:ExpenseDetails ui:field='expenseDetails' />
+        </e:SlidingPanel>
       </g:center>
     </g:DockLayoutPanel>
   </g:center>
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java
index 0ba748d..971701a 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseDetails.java
@@ -23,8 +23,8 @@
 import com.google.gwt.requestfactory.shared.EntityProxyChange;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.sample.expenses.client.request.ExpenseProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ExpenseProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.Composite;
@@ -114,17 +114,17 @@
     @SuppressWarnings("deprecation")
     DateTimeFormat formatter = DateTimeFormat.getMediumDateFormat();
 
-    Expenses.Approval approval = Expenses.Approval.from(expense.getApproval());
+    Approval approval = Approval.from(expense.getApproval());
     nameText.setInnerText(expense.getDescription());
     dateText.setInnerText(formatter.format(expense.getCreated()));
     categoryText.setInnerText(expense.getCategory());
     priceText.setInnerText(ExpensesMobile.formatCurrency(expense.getAmount()));
-    approvalText.setInnerHTML(Expenses.Approval.BLANK.equals(approval)
+    approvalText.setInnerHTML(Approval.BLANK.equals(approval)
         ? "Awaiting Review" : approval.getText());
     approvalText.getStyle().setColor(approval.getColor());
 
     reasonText.setInnerText(expense.getReasonDenied());
-    if (Expenses.Approval.DENIED.equals(approval)) {
+    if (Approval.DENIED.equals(approval)) {
       // Show the reason denied.
       reasonRow.getStyle().clearDisplay();
     } else {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java
index 5aeddba..88df8d7 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileExpenseEntry.java
@@ -18,10 +18,10 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.sample.expenses.client.request.ExpenseProxy;
-import com.google.gwt.sample.expenses.client.request.ExpenseRequest;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseRequest;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.Composite;
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 1b4f8f5..df44ef9 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
@@ -18,12 +18,13 @@
 import com.google.gwt.cell.client.AbstractCell;
 import com.google.gwt.core.client.GWT;
 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.shared.SafeHtml;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.sample.expenses.client.request.ExpenseProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
+import com.google.gwt.sample.expenses.shared.ExpenseProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
 import com.google.gwt.user.cellview.client.CellList;
 import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
 import com.google.gwt.user.client.Timer;
@@ -65,18 +66,18 @@
   private class ExpenseCell extends AbstractCell<ExpenseProxy> {
 
     private final SafeHtml approvedHtml;
-    private final String approvedText = Expenses.Approval.APPROVED.getText();
+    private final String approvedText = Approval.APPROVED.getText();
     private final SafeHtml blankHtml;
     private final SafeHtml deniedHtml;
-    private final String deniedText = Expenses.Approval.DENIED.getText();
+    private final String deniedText = Approval.DENIED.getText();
 
     public ExpenseCell() {
       if (template == null) {
         template = GWT.create(Template.class);
       }
-      approvedHtml = Expenses.Approval.APPROVED.getIconHtml();
-      blankHtml = Expenses.Approval.BLANK.getIconHtml();
-      deniedHtml = Expenses.Approval.DENIED.getIconHtml();
+      approvedHtml = Approval.APPROVED.getIconHtml();
+      blankHtml = Approval.BLANK.getIconHtml();
+      deniedHtml = Approval.DENIED.getIconHtml();
     }
 
     @Override
@@ -135,7 +136,7 @@
       final Listener listener, final ExpensesRequestFactory requestFactory) {
     this.listener = listener;
     this.requestFactory = requestFactory;
-    expenseDataProvider = new AsyncDataProvider<ExpenseProxy>(Expenses.EXPENSE_RECORD_KEY_PROVIDER) {
+    expenseDataProvider = new AsyncDataProvider<ExpenseProxy>(new EntityProxyKeyProvider<ExpenseProxy>()) {
       @Override
       protected void onRangeChanged(HasData<ExpenseProxy> view) {
         requestExpenses();
@@ -230,7 +231,7 @@
           for (ExpenseProxy value : newValues) {
             Object key = expenseDataProvider.getKey(value);
             String approval = value.getApproval();
-            if (Expenses.Approval.DENIED.getText().equals(approval)) {
+            if (Approval.DENIED.getText().equals(approval)) {
               if (!isInitialData && !knownDeniedKeys.contains(key)) {
                 (new PhaseAnimation.CellListPhaseAnimation<ExpenseProxy>(
                     expenseList, value, expenseDataProvider)).run();
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java
index 96194e5..8d541a1 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/MobileReportEntry.java
@@ -18,10 +18,10 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
-import com.google.gwt.sample.expenses.client.request.ReportRequest;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
+import com.google.gwt.sample.expenses.shared.ReportRequest;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.Composite;
@@ -67,7 +67,7 @@
     this.requestFactory = requestFactory;
     initWidget(BINDER.createAndBindUi(this));
 
-    for (String department : Expenses.DEPARTMENTS) {
+    for (String department : ExpensesApp.DEPARTMENTS) {
       departmentList.addItem(department);
     }
 
@@ -144,8 +144,8 @@
     notesText.setText(report.getNotes());
     String department = report.getDepartment();
     departmentList.setSelectedIndex(0);
-    for (int i = 0; i < Expenses.DEPARTMENTS.length; i++) {
-      if (Expenses.DEPARTMENTS[i].equals(department)) {
+    for (int i = 0; i < ExpensesApp.DEPARTMENTS.length; i++) {
+      if (ExpensesApp.DEPARTMENTS[i].equals(department)) {
         departmentList.setSelectedIndex(i);
       }
     }
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 dc50155..4ef274c 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
@@ -17,10 +17,11 @@
 
 import com.google.gwt.cell.client.AbstractCell;
 import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.requestfactory.ui.client.EntityProxyKeyProvider;
 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
-import com.google.gwt.sample.expenses.client.request.EmployeeProxy;
-import com.google.gwt.sample.expenses.client.request.ExpensesRequestFactory;
-import com.google.gwt.sample.expenses.client.request.ReportProxy;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
 import com.google.gwt.user.cellview.client.CellList;
 import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
 import com.google.gwt.user.client.ui.Composite;
@@ -63,8 +64,10 @@
     this.listener = listener;
     this.requestFactory = requestFactory;
     this.employee = employee;
+    
+    EntityProxyKeyProvider<ReportProxy> keyProvider = new EntityProxyKeyProvider<ReportProxy>();
 
-    reportDataProvider = new AsyncDataProvider<ReportProxy>(Expenses.REPORT_RECORD_KEY_PROVIDER) {
+    reportDataProvider = new AsyncDataProvider<ReportProxy>(keyProvider) {
       @Override
       protected void onRangeChanged(HasData<ReportProxy> view) {
         requestReports();
@@ -82,7 +85,7 @@
     });
     reportList.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);
 
-    reportSelection = new NoSelectionModel<ReportProxy>(Expenses.REPORT_RECORD_KEY_PROVIDER);
+    reportSelection = new NoSelectionModel<ReportProxy>(keyProvider);
     reportSelection.addSelectionChangeHandler(
         new SelectionChangeEvent.Handler() {
           public void onSelectionChange(SelectionChangeEvent event) {
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SlidingPanel.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SlidingPanel.java
new file mode 100644
index 0000000..6b8d18b
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/SlidingPanel.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.sample.expenses.client;
+
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.layout.client.Layout;
+import com.google.gwt.layout.client.Layout.Layer;
+import com.google.gwt.user.client.ui.HasOneWidget;
+import com.google.gwt.user.client.ui.HasWidgets;
+import com.google.gwt.user.client.ui.IsWidget;
+import com.google.gwt.user.client.ui.LayoutPanel;
+import com.google.gwt.user.client.ui.ResizeComposite;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Shows one panel at a time, sliding them left or right based on the order they
+ * were added. A full fledged version might implement
+ * {@link com.google.gwt.user.client.ui.InsertPanel.ForIsWidget}.
+ * <p>
+ * Note that we implement HasWidgets so that SlidingPanel will work nicely in
+ * ui.xml files.
+ */
+public class SlidingPanel extends ResizeComposite implements HasWidgets,
+    HasOneWidget {
+
+  private final List<Widget> widgets = new ArrayList<Widget>();
+  private final LayoutPanel layoutPanel = new LayoutPanel();
+  private int currentIndex = -1;
+
+  public SlidingPanel() {
+    initWidget(layoutPanel);
+  }
+
+  public void add(IsWidget w) {
+    add(w.asWidget());
+  }
+
+  public void add(Widget w) {
+    widgets.remove(w);
+    widgets.add(w);
+
+    // Display the first widget added by default
+    if (currentIndex < 0) {
+      layoutPanel.add(w);
+      currentIndex = 0;
+    }
+  }
+
+  public void clear() {
+    setWidget(null);
+    widgets.clear();
+  }
+
+  public Widget getWidget() {
+    return widgets.get(currentIndex);
+  }
+
+  public Iterator<Widget> iterator() {
+    return Collections.unmodifiableList(widgets).iterator();
+  }
+
+  public boolean remove(Widget w) {
+    return widgets.remove(w);
+  }
+
+  public void setWidget(IsWidget w) {
+    setWidget(w.asWidget());
+  }
+
+  // Conflict btw deprecated Composite#setWidget and HasOneWidget#setWidget
+  @SuppressWarnings("deprecation")
+  public void setWidget(Widget widget) {
+    int newIndex = widgets.indexOf(widget);
+
+    if (newIndex < 0) {
+      newIndex = widgets.size();
+      add(widget);
+    }
+
+    show(newIndex);
+  }
+
+  private void show(int newIndex) {
+    if (newIndex == currentIndex) {
+      return;
+    }
+
+    boolean fromLeft = newIndex < currentIndex;
+    currentIndex = newIndex;
+
+    Widget widget = widgets.get(newIndex);
+    final Widget current = layoutPanel.getWidget(0);
+
+    // Initialize the layout.
+    layoutPanel.add(widget);
+    layoutPanel.setWidgetLeftWidth(current, 0, Unit.PCT, 100, Unit.PCT);
+    if (fromLeft) {
+      layoutPanel.setWidgetLeftWidth(widget, -100, Unit.PCT, 100, Unit.PCT);
+    } else {
+      layoutPanel.setWidgetLeftWidth(widget, 100, Unit.PCT, 100, Unit.PCT);
+    }
+    layoutPanel.forceLayout();
+
+    // Slide into view.
+    if (fromLeft) {
+      layoutPanel.setWidgetLeftWidth(current, 100, Unit.PCT, 100, Unit.PCT);
+    } else {
+      layoutPanel.setWidgetLeftWidth(current, -100, Unit.PCT, 100, Unit.PCT);
+    }
+    layoutPanel.setWidgetLeftWidth(widget, 0, Unit.PCT, 100, Unit.PCT);
+    layoutPanel.animate(500, new Layout.AnimationCallback() {
+      public void onAnimationComplete() {
+        // Remove the old widget when the animation completes.
+        layoutPanel.remove(current);
+      }
+
+      public void onLayout(Layer layer, double progress) {
+      }
+    });
+  }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/Factory.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/Factory.java
new file mode 100644
index 0000000..f2dfc9e
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ioc/Factory.java
@@ -0,0 +1,64 @@
+/*
+ * 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.expenses.client.ioc;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.event.shared.SimpleEventBus;
+import com.google.gwt.place.shared.PlaceController;
+import com.google.gwt.place.shared.PlaceHistoryHandler;
+import com.google.gwt.sample.expenses.client.ExpenseDetails;
+import com.google.gwt.sample.expenses.client.ExpenseTree;
+import com.google.gwt.sample.expenses.client.ExpensesApp;
+import com.google.gwt.sample.expenses.client.ExpensesShell;
+import com.google.gwt.sample.expenses.client.place.ExpensesPlaceHistoryMapper;
+import com.google.gwt.sample.expenses.client.place.ReportListPlace;
+import com.google.gwt.sample.expenses.client.place.ReportPlace;
+import com.google.gwt.sample.expenses.shared.ExpensesRequestFactory;
+
+/**
+ * In charge of instantiation.
+ * <p>
+ * TODO: Use {@link http ://code.google.com/p/google-gin/} to generate this
+ */
+public class Factory {
+
+  private final EventBus eventBus = new SimpleEventBus();
+  private final ExpensesRequestFactory requestFactory = GWT.create(ExpensesRequestFactory.class);
+  private final ExpensesPlaceHistoryMapper historyMapper = GWT.create(ExpensesPlaceHistoryMapper.class);
+  private final PlaceHistoryHandler placeHistoryHandler;
+  private final PlaceController placeController = new PlaceController(eventBus);
+
+  public Factory() {
+    requestFactory.initialize(eventBus);
+    historyMapper.setFactory(this);
+    placeHistoryHandler = new PlaceHistoryHandler(historyMapper);
+  }
+
+  public ExpensesApp getExpensesApp() {
+    return new ExpensesApp(requestFactory, eventBus, new ExpensesShell(
+        new ExpenseTree(requestFactory), new ExpenseDetails(requestFactory)),
+        placeHistoryHandler, placeController);
+  }
+
+  public ReportListPlace.Tokenizer getListTokenizer() {
+    return new ReportListPlace.Tokenizer(requestFactory);
+  }
+
+  public ReportPlace.Tokenizer getReportTokenizer() {
+    return new ReportPlace.Tokenizer(getListTokenizer(), requestFactory);
+  }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyEditActivity.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyEditActivity.java
deleted file mode 100644
index a2c8ada..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyEditActivity.java
+++ /dev/null
@@ -1,186 +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.expenses.client.place;
-
-import com.google.gwt.activity.shared.Activity;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.place.shared.PlaceController;
-import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.EntityProxyId;
-import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.RequestContext;
-import com.google.gwt.requestfactory.shared.ServerFailure;
-import com.google.gwt.requestfactory.shared.Violation;
-import com.google.gwt.sample.expenses.client.place.ProxyPlace.Operation;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.AcceptsOneWidget;
-
-import java.util.Set;
-
-/**
- * Abstract activity for editing a record. Subclasses must provide access to the
- * request that will be fired when Save is clicked.
- * <p>
- * Instances are not reusable. Once an activity is stoped, it cannot be
- * restarted.
- * 
- * @param <P> the type of Proxy being edited
- */
-public abstract class AbstractProxyEditActivity<P extends EntityProxy> implements Activity,
-    ProxyEditView.Delegate {
-
-  private final ProxyEditView<P, ?> view;
-  private final PlaceController placeController;
-
-  private RequestFactoryEditorDriver<P, ?> editorDriver;
-  private boolean waiting;
-
-  public AbstractProxyEditActivity(ProxyEditView<P, ?> view, PlaceController placeController) {
-    this.view = view;
-    this.placeController = placeController;
-  }
-
-  public void cancelClicked() {
-    String unsavedChangesWarning = mayStop();
-    if ((unsavedChangesWarning == null)
-        || Window.confirm(unsavedChangesWarning)) {
-      editorDriver = null;
-      exit(false);
-    }
-  }
-
-  public String mayStop() {
-    if (isWaiting() || changed()) {
-      return "Are you sure you want to abandon your changes?";
-    }
-
-    return null;
-  }
-
-  public void onCancel() {
-    onStop();
-  }
-
-  public void onStop() {
-    view.setDelegate(null);
-    editorDriver = null;
-  }
-
-  public void saveClicked() {
-    if (!changed()) {
-      return;
-    }
-    
-    setWaiting(true);
-    editorDriver.flush().fire(new Receiver<Void>() {
-      /*
-       * Callbacks do nothing if editorDriver is null, we were stopped in
-       * midflight
-       */
-      @Override
-      public void onFailure(ServerFailure error) {
-        if (editorDriver != null) {
-          setWaiting(false);
-          super.onFailure(error);
-        }
-      }
-
-      @Override
-      public void onSuccess(Void ignore) {
-        if (editorDriver != null) {
-          // We want no warnings from mayStop, so:
-
-          // Defeat isChanged check
-          editorDriver = null;
-
-          // Defeat call-in-flight check
-          setWaiting(false);
-
-          exit(true);
-        }
-      }
-
-      @Override
-      public void onViolation(Set<Violation> errors) {
-        if (editorDriver != null) {
-          setWaiting(false);
-          editorDriver.setViolations(errors);
-        }
-      }
-    });
-  }
-
-  public void start(AcceptsOneWidget display, EventBus eventBus) {
-    editorDriver = view.createEditorDriver();
-    view.setDelegate(this);
-    editorDriver.edit(getProxy(), createSaveRequest(getProxy()));
-    display.setWidget(view);
-  }
-
-  /**
-   * Called once to create the appropriate request to save
-   * changes.
-   * 
-   * @return the request context to fire when the save button is clicked
-   */
-  protected abstract RequestContext createSaveRequest(P proxy);
-
-  /**
-   * Called when the user cancels or has successfully saved. This default
-   * implementation tells the {@link PlaceController} to show the details of the
-   * edited record.
-   * 
-   * @param saved true if changes were comitted, false if user canceled
-   */
-  protected void exit(@SuppressWarnings("unused") boolean saved) {
-    placeController.goTo(new ProxyPlace(getProxyId(), Operation.DETAILS));
-  }
-
-  /**
-   * Get the proxy to be edited. Must be mutable, typically via a call to
-   * {@link RequestContext#edit(EntityProxy)}, or
-   * {@link RequestContext#create(Class)}.
-   */
-  protected abstract P getProxy();
-
-  @SuppressWarnings("unchecked")
-  // id type always matches proxy type
-  protected EntityProxyId<P> getProxyId() {
-    return (EntityProxyId<P>) getProxy().stableId();
-  }
-
-  private boolean changed() {
-    return editorDriver != null && editorDriver.flush().isChanged();
-  }
-
-  /**
-   * @return true if we're waiting for an rpc response.
-   */
-  private boolean isWaiting() {
-    return waiting;
-  }
-
-  /**
-   * While we are waiting for a response, we cannot poke setters on the proxy
-   * (that is, we cannot call editorDriver.flush). So we set the waiting flag to
-   * warn ourselves not to, and to disable the view.
-   */
-  private void setWaiting(boolean wait) {
-    this.waiting = wait;
-    view.setEnabled(!wait);
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyListActivity.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyListActivity.java
deleted file mode 100644
index 5058fa1..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyListActivity.java
+++ /dev/null
@@ -1,315 +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.expenses.client.place;
-
-import com.google.gwt.activity.shared.Activity;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.place.shared.Place;
-import com.google.gwt.place.shared.PlaceChangeEvent;
-import com.google.gwt.place.shared.PlaceController;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.EntityProxyChange;
-import com.google.gwt.requestfactory.shared.EntityProxyId;
-import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.Request;
-import com.google.gwt.requestfactory.shared.WriteOperation;
-import com.google.gwt.sample.expenses.client.place.ProxyPlace.Operation;
-import com.google.gwt.user.cellview.client.AbstractHasData;
-import com.google.gwt.user.client.ui.AcceptsOneWidget;
-import com.google.gwt.view.client.HasData;
-import com.google.gwt.view.client.ProvidesKey;
-import com.google.gwt.view.client.Range;
-import com.google.gwt.view.client.RangeChangeEvent;
-import com.google.gwt.view.client.SelectionChangeEvent;
-import com.google.gwt.view.client.SingleSelectionModel;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Abstract activity for displaying a list of {@link EntityProxy}. These
- * activities are not re-usable. Once they are stopped, they cannot be
- * restarted.
- * <p>
- * Subclasses must:
- * 
- * <ul>
- * <li>provide a {@link ProxyListView}
- * <li>implement method to request a full count
- * <li>implement method to find a range of entities
- * <li>respond to "show details" commands
- * </ul>
- * <p>
- * Only the properties required by the view will be requested.
- * 
- * @param <P> the type of {@link EntityProxy} listed
- */
-public abstract class AbstractProxyListActivity<P extends EntityProxy>
-    implements Activity, ProxyListView.Delegate<P> {
-
-  /**
-   * This mapping allows us to update individual rows as records change.
-   */
-  private final Map<EntityProxyId<P>, Integer> idToRow = new HashMap<EntityProxyId<P>, Integer>();
-  private final Map<EntityProxyId<P>, P> idToProxy = new HashMap<EntityProxyId<P>, P>();
-
-  private final PlaceController placeController;
-  private final SingleSelectionModel<P> selectionModel;
-  private final Class<P> proxyClass;
-
-  private HandlerRegistration rangeChangeHandler;
-  private ProxyListView<P> view;
-  private AcceptsOneWidget display;
-
-  public AbstractProxyListActivity(PlaceController placeController,
-      ProxyListView<P> view, Class<P> proxyType) {
-    this.view = view;
-    this.placeController = placeController;
-    this.proxyClass = proxyType;
-    view.setDelegate(this);
-
-    final HasData<P> hasData = view.asHasData();
-    rangeChangeHandler = hasData.addRangeChangeHandler(new RangeChangeEvent.Handler() {
-      public void onRangeChange(RangeChangeEvent event) {
-        AbstractProxyListActivity.this.onRangeChanged(hasData);
-      }
-    });
-
-    // Inherit the view's key provider
-    ProvidesKey<P> keyProvider = ((AbstractHasData<P>) hasData).getKeyProvider();
-    selectionModel = new SingleSelectionModel<P>(keyProvider);
-    hasData.setSelectionModel(selectionModel);
-
-    selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
-      public void onSelectionChange(SelectionChangeEvent event) {
-        P selectedObject = selectionModel.getSelectedObject();
-        if (selectedObject != null) {
-          showDetails(selectedObject);
-        }
-      }
-    });
-  }
-
-  public void createClicked() {
-    placeController.goTo(new ProxyPlace(proxyClass));
-  }
-
-  public ProxyListView<P> getView() {
-    return view;
-  }
-
-  public String mayStop() {
-    return null;
-  }
-
-  public void onCancel() {
-    onStop();
-  }
-
-  /**
-   * Called by the table as it needs data.
-   */
-  public void onRangeChanged(HasData<P> listView) {
-    final Range range = listView.getVisibleRange();
-
-    final Receiver<List<P>> callback = new Receiver<List<P>>() {
-      @Override
-      public void onSuccess(List<P> values) {
-        if (view == null) {
-          // This activity is dead
-          return;
-        }
-        idToRow.clear();
-        idToProxy.clear();
-        for (int i = 0, row = range.getStart(); i < values.size(); i++, row++) {
-          P proxy = values.get(i);
-          @SuppressWarnings("unchecked")
-          // Why is this cast needed?
-          EntityProxyId<P> proxyId = (EntityProxyId<P>) proxy.stableId();
-          idToRow.put(proxyId, row);
-          idToProxy.put(proxyId, proxy);
-        }
-        getView().asHasData().setRowData(range.getStart(), values);
-        if (display != null) {
-          display.setWidget(getView());
-        }
-      }
-    };
-
-    fireRangeRequest(range, callback);
-  }
-
-  public void onStop() {
-    view.setDelegate(null);
-    view = null;
-    rangeChangeHandler.removeHandler();
-    rangeChangeHandler = null;
-  }
-
-  /**
-   * Select the given record, or clear the selection if called with null or an
-   * id we don't know.
-   */
-  public void select(EntityProxyId<P> proxyId) {
-    /*
-     * The selectionModel will not flash if we put it back to the same state it
-     * is already in, so we can keep this code simple.
-     */
-
-    // Clear the selection
-    P selected = selectionModel.getSelectedObject();
-    if (selected != null) {
-      selectionModel.setSelected(selected, false);
-    }
-
-    // Select the new proxy, if it's relevant
-    if (proxyId != null) {
-      P selectMe = idToProxy.get(proxyId);
-      selectionModel.setSelected(selectMe, true);
-    }
-  }
-
-  public void start(AcceptsOneWidget display, EventBus eventBus) {
-    view.setDelegate(this);
-    EntityProxyChange.registerForProxyType(eventBus, proxyClass,
-        new EntityProxyChange.Handler<P>() {
-          public void onProxyChange(EntityProxyChange<P> event) {
-            update(event.getWriteOperation(), event.getProxyId());
-          }
-        });
-    eventBus.addHandler(PlaceChangeEvent.TYPE, new PlaceChangeEvent.Handler() {
-      public void onPlaceChange(PlaceChangeEvent event) {
-        updateSelection(event.getNewPlace());
-      }
-    });
-    this.display = display;
-    init();
-    updateSelection(placeController.getWhere());
-  }
-
-  public void update(WriteOperation writeOperation, EntityProxyId<P> proxyId) {
-    switch (writeOperation) {
-      case UPDATE:
-        update(proxyId);
-        break;
-
-      case DELETE:
-        init();
-        break;
-
-      case PERSIST:
-        /*
-         * On create, we presume the new record is at the end of the list, so
-         * fetch the last page of items.
-         */
-        getLastPage();
-        break;
-    }
-  }
-
-  protected abstract Request<List<P>> createRangeRequest(Range range);
-
-  protected abstract void fireCountRequest(Receiver<Long> callback);
-
-  /**
-   * Called when the user chooses a record to view. This default implementation
-   * sends the {@link PlaceController} to an appropriate {@link ProxyPlace}.
-   * 
-   * @param record the chosen record
-   */
-  protected void showDetails(P record) {
-    placeController.goTo(new ProxyPlace(record.stableId(), Operation.DETAILS));
-  }
-
-  @SuppressWarnings("unchecked")
-  private EntityProxyId<P> cast(ProxyPlace proxyPlace) {
-    return (EntityProxyId<P>) proxyPlace.getProxyId();
-  }
-
-  private void fireRangeRequest(final Range range,
-      final Receiver<List<P>> callback) {
-    createRangeRequest(range).with(getView().getPaths()).fire(callback);
-  }
-
-  private void getLastPage() {
-    fireCountRequest(new Receiver<Long>() {
-      @Override
-      public void onSuccess(Long response) {
-        if (view == null) {
-          // This activity is dead
-          return;
-        }
-        HasData<P> table = getView().asHasData();
-        int rows = response.intValue();
-        table.setRowCount(rows, true);
-        if (rows > 0) {
-          int pageSize = table.getVisibleRange().getLength();
-          int remnant = rows % pageSize;
-          if (remnant == 0) {
-            table.setVisibleRange(rows - pageSize, pageSize);
-          } else {
-            table.setVisibleRange(rows - remnant, pageSize);
-          }
-        }
-        onRangeChanged(table);
-      }
-    });
-  }
-
-  private void init() {
-    fireCountRequest(new Receiver<Long>() {
-      @Override
-      public void onSuccess(Long response) {
-        if (view == null) {
-          // This activity is dead
-          return;
-        }
-        getView().asHasData().setRowCount(response.intValue(), true);
-        onRangeChanged(view.asHasData());
-      }
-    });
-  }
-
-  private void update(EntityProxyId<P> proxyId) {
-    final Integer row = idToRow.get(proxyId);
-    if (row == null) {
-      return;
-    }
-    fireRangeRequest(new Range(row, 1), new Receiver<List<P>>() {
-      @Override
-      public void onSuccess(List<P> response) {
-        getView().asHasData().setRowData(row,
-            Collections.singletonList(response.get(0)));
-      }
-    });
-  }
-
-  private void updateSelection(Place newPlace) {
-    if (newPlace instanceof ProxyPlace) {
-      ProxyPlace proxyPlace = (ProxyPlace) newPlace;
-      if (proxyPlace.getOperation() != Operation.CREATE
-          && proxyPlace.getProxyClass().equals(proxyClass)) {
-        select(cast(proxyPlace));
-        return;
-      }
-    }
-
-    select(null);
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyListView.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyListView.java
deleted file mode 100644
index c05fdd8..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/AbstractProxyListView.java
+++ /dev/null
@@ -1,67 +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.expenses.client.place;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.view.client.HasData;
-
-/**
- * Abstract implementation of ProxyListView.
- *
- * @param <P> the type of the proxy
- */
-public abstract class AbstractProxyListView<P extends EntityProxy> extends
-    Composite implements ProxyListView<P> {
-
-  private HasData<P> display;
-  private Delegate<P> delegate;
-
-  public HasData<P> asHasData() {
-    return display;
-  }
-
-  @Override
-  public AbstractProxyListView<P> asWidget() {
-    return this;
-  }
-
-  public void setDelegate(final Delegate<P> delegate) {
-    this.delegate = delegate;
-  }
-
-  protected void init(Widget root, HasData<P> display, Button newButton) {
-    super.initWidget(root);
-    this.display = display;
-
-    newButton.addClickHandler(new ClickHandler() {
-      public void onClick(ClickEvent event) {
-        delegate.createClicked();
-      }
-    });
-  }
-
-  @Override
-  protected void initWidget(Widget widget) {
-    throw new UnsupportedOperationException(
-        "AbstractRecordListView must be initialized via "
-            + "init(Widget, HasData<P>, Button) ");
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/CreateAndEditProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/CreateAndEditProxy.java
deleted file mode 100644
index e8fed9b..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/CreateAndEditProxy.java
+++ /dev/null
@@ -1,68 +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.expenses.client.place;
-
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.place.shared.PlaceController;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.RequestContext;
-import com.google.gwt.user.client.ui.AcceptsOneWidget;
-
-/**
- * Extends {@link AbstractProxyEditActivity} to first create an instance to
- * edit.
- * 
- * @param <P> the type of proxy to create and edit
- */
-public abstract class CreateAndEditProxy<P extends EntityProxy> extends AbstractProxyEditActivity<P> {
-
-  private final P proxy;
-  private final PlaceController placeController;
-  private Class<P> proxyClass;
-
-  public CreateAndEditProxy(Class<P> proxyClass, RequestContext request,
-      ProxyEditView<P, ?> view, PlaceController placeController) {
-    super(view, placeController);
-    this.proxy = request.create(proxyClass);
-    this.placeController = placeController;
-    this.proxyClass = proxyClass;
-  }
-
-  @Override
-  public void start(AcceptsOneWidget display, EventBus eventBus) {
-    super.start(display, eventBus);
-  }
-
-  /**
-   * Called when the user cancels or has successfully saved. Refines the default
-   * implementation to clear the display given at {@link #start} on cancel.
-   * 
-   * @param saved true if changes were comitted, false if user canceled
-   */
-  @Override
-  protected void exit(boolean saved) {
-    if (!saved) {
-      placeController.goTo(new ProxyListPlace(proxyClass));
-    } else {
-      super.exit(saved);
-    }
-  }
-
-  @Override
-  protected P getProxy() {
-    return proxy;
-  }  
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ExpensesPlaceHistoryMapper.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ExpensesPlaceHistoryMapper.java
new file mode 100644
index 0000000..d5d16de
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ExpensesPlaceHistoryMapper.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.sample.expenses.client.place;
+
+import com.google.gwt.place.shared.PlaceHistoryMapperWithFactory;
+import com.google.gwt.sample.expenses.client.ioc.Factory;
+
+/**
+ * This interface is the hub of your application's navigation system. It links
+ * the {@link com.google.gwt.place.shared.Place Place}s your user navigates to
+ * with the browser history system &mdash; that is, it makes the browser's back
+ * and forth buttons work for you, and also makes each spot in your app
+ * bookmarkable.
+ * 
+ * <p>
+ * The simplest way to make new {@link com.google.gwt.place.shared.Place Place}
+ * types available to your app is to uncomment the {@literal @}WithTokenizers
+ * annotation below and list their corresponding
+ * {@link com.google.gwt.place.shared.PlaceTokenizer PlaceTokenizer}s. Or if a
+ * tokenizer needs more than a default constructor can provide, add a method to
+ * the apps {@link Factory}.
+ * 
+ * <p>
+ * This code generated object looks to both the {@literal @}WithTokenizers
+ * annotation and the factory to infer the types of
+ * {@link com.google.gwt.place.Place Place}s your app can navigate to. In this
+ * case it will find the {@link Factory#getListTokenizer()} and
+ * {@link Factory#getReportTokenizer()} methods, and so be able to handle
+ * {@link ReportListPlace}s and {@link ReportPlace}s.
+ */
+// @WithTokenizers({MyNewPlace.Tokenizer, MyOtherNewPlace.Tokenizer})
+public interface ExpensesPlaceHistoryMapper extends
+    PlaceHistoryMapperWithFactory<Factory> {
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/FindAndEditProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/FindAndEditProxy.java
deleted file mode 100644
index a86ba0c..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/FindAndEditProxy.java
+++ /dev/null
@@ -1,61 +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.expenses.client.place;
-
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.place.shared.PlaceController;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.EntityProxyId;
-import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.RequestFactory;
-import com.google.gwt.user.client.ui.AcceptsOneWidget;
-
-/**
- * Extends {@link AbstractProxyEditActivity} to work from a
- * {@link EntityProxyId}.
- * 
- * @param <P> the type of proxy to find and edit
- */
-public abstract class FindAndEditProxy<P extends EntityProxy> extends
-    AbstractProxyEditActivity<P> {
-
-  private final RequestFactory factory;
-  private final EntityProxyId<P> proxyId;
-  private P proxy;
-
-  public FindAndEditProxy(EntityProxyId<P> proxyId, RequestFactory factory,
-      ProxyEditView<P, ?> view, PlaceController placeController) {
-    super(view, placeController);
-    this.proxyId = proxyId;
-    this.factory = factory;
-  }
-
-  @Override
-  public void start(final AcceptsOneWidget display, final EventBus eventBus) {
-    factory.find(proxyId).fire(new Receiver<P>() {
-      @Override
-      public void onSuccess(P response) {
-        proxy = response;
-        FindAndEditProxy.super.start(display, eventBus);
-      }
-    });
-  }
-
-  @Override
-  protected P getProxy() {
-    return proxy;
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyDetailsView.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyDetailsView.java
deleted file mode 100644
index 2b660d4..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyDetailsView.java
+++ /dev/null
@@ -1,39 +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.expenses.client.place;
-
-import com.google.gwt.user.client.TakesValue;
-import com.google.gwt.user.client.ui.IsWidget;
-
-/**
- * Implemented by views that show the details of an object.
- *
- * @param <P> the type of object to show
- */
-public interface ProxyDetailsView<P> extends TakesValue<P>, IsWidget {
-
-  /**
-   * Implemented by the owner of the view.
-   */
-  interface Delegate {
-    void deleteClicked();
-    void editClicked();
-  }
-  
-  boolean confirm(String msg);
-  
-  void setDelegate(Delegate delegate);
-}
\ No newline at end of file
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyEditView.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyEditView.java
deleted file mode 100644
index 70e9c6d..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyEditView.java
+++ /dev/null
@@ -1,51 +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.expenses.client.place;
-
-import com.google.gwt.editor.client.HasEditorErrors;
-import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.user.client.ui.IsWidget;
-
-/**
- * Implemented by views that edit {@link EntityProxy}s.
- * 
- * @param <P> the type of the proxy
- * @param <V> the type of this ProxyEditView, required to allow
- *          {@link #createEditorDriver()} to be correctly typed
- */
-public interface ProxyEditView<P extends EntityProxy, V extends ProxyEditView<P, V>>
-    extends IsWidget, HasEditorErrors<P> {
-
-  /**
-   * @return a new {@link RequestFactoryEditorDriver} initialized to run this
-   *         editor
-   */
-  RequestFactoryEditorDriver<P, V> createEditorDriver();
-
-  /**
-   * Implemented by the owner of the view.
-   */
-  interface Delegate {
-    void cancelClicked();
-
-    void saveClicked();
-  }
-
-  void setDelegate(Delegate delegate);
-
-  void setEnabled(boolean b);
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListPlace.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListPlace.java
deleted file mode 100644
index 8167386..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListPlace.java
+++ /dev/null
@@ -1,89 +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.expenses.client.place;
-
-import com.google.gwt.place.shared.Place;
-import com.google.gwt.place.shared.PlaceTokenizer;
-import com.google.gwt.place.shared.Prefix;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.RequestFactory;
-
-/**
- * A place in the app that deals with lists of {@link EntityProxy}.
- */
-public class ProxyListPlace extends Place {
-
-  /**
-   * Tokenizer.
-   */
-  @Prefix("l")
-  public static class Tokenizer implements PlaceTokenizer<ProxyListPlace> {
-    private final RequestFactory requests;
-
-    public Tokenizer(RequestFactory requests) {
-      this.requests = requests;
-    }
-
-    public ProxyListPlace getPlace(String token) {
-      return new ProxyListPlace(requests.getProxyClass(token));
-    }
-
-    public String getToken(ProxyListPlace place) {
-      return requests.getHistoryToken(place.getProxyClass());
-    }
-  }
-
-  private final Class<? extends EntityProxy> proxyType;
-
-  public ProxyListPlace(Class<? extends EntityProxy> proxyType) {
-    this.proxyType = proxyType;
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (this == obj) {
-      return true;
-    }
-    if (obj == null) {
-      return false;
-    }
-    if (getClass() != obj.getClass()) {
-      return false;
-    }
-    ProxyListPlace other = (ProxyListPlace) obj;
-    if (!proxyType.equals(other.proxyType)) {
-      return false;
-    }
-    return true;
-  }
-
-  public Class<? extends EntityProxy> getProxyClass() {
-    return proxyType;
-  }
-
-  @Override
-  public int hashCode() {
-    final int prime = 31;
-    int result = 1;
-    result = prime * result + proxyType.hashCode();
-    return result;
-  }
-
-  @Override
-  public String toString() {
-    return "ProxyListPlace [proxyType=" + proxyType + "]";
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListPlacePicker.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListPlacePicker.java
deleted file mode 100644
index 0f1d3c6..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListPlacePicker.java
+++ /dev/null
@@ -1,77 +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.expenses.client.place;
-
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.place.shared.PlaceChangeEvent;
-import com.google.gwt.place.shared.PlaceController;
-import com.google.gwt.user.client.ui.HasConstrainedValue;
-
-/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Drives a {@link ValueChangeHandler} populated with {@link ProxyListPlace}
- * instances, keeping it in sync with the {@link PlaceController}'s notion of
- * the current place, and firing place change events as selections are made.
- */
-public class ProxyListPlacePicker implements ValueChangeHandler<ProxyListPlace>,
-    PlaceChangeEvent.Handler {
-
-  private HasConstrainedValue<ProxyListPlace> view;
-
-  private final PlaceController placeController;
-  private final ProxyPlaceToListPlace proxyPlaceToListPlace;
-
-  public ProxyListPlacePicker(PlaceController placeController,
-      ProxyPlaceToListPlace proxyPlaceToListPlace) {
-    this.placeController = placeController;
-    this.proxyPlaceToListPlace = proxyPlaceToListPlace;
-  }
-
-  public void onPlaceChange(PlaceChangeEvent event) {
-    view.setValue(proxyPlaceToListPlace.proxyListPlaceFor(event.getNewPlace()),
-        false);
-  }
-
-  public void onValueChange(ValueChangeEvent<ProxyListPlace> event) {
-    placeController.goTo(event.getValue());
-  }
-
-  public HandlerRegistration register(EventBus eventBus,
-      HasConstrainedValue<ProxyListPlace> view) {
-
-    this.view = view;
-
-    final HandlerRegistration placeRegistration = eventBus.addHandler(
-        PlaceChangeEvent.TYPE, this);
-
-    final HandlerRegistration viewRegistration = view.addValueChangeHandler(this);
-
-    return new HandlerRegistration() {
-      public void removeHandler() {
-        placeRegistration.removeHandler();
-        viewRegistration.removeHandler();
-        ProxyListPlacePicker.this.view = null;
-      }
-    };
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListView.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListView.java
deleted file mode 100644
index 6156ff3..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyListView.java
+++ /dev/null
@@ -1,58 +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.expenses.client.place;
-
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.user.client.ui.IsWidget;
-import com.google.gwt.view.client.HasData;
-
-/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * A view of a list of {@link EntityProxy}s, which declares which properties it
- * is able to display.
- * <p>
- * It is expected that such views will typically (eventually) be defined largely
- * in ui.xml files which declare the properties of interest, which is why the
- * view is a source of a property set rather than a receiver of one.
- * 
- * @param <P> the type of the records to display
- */
-public interface ProxyListView<P extends EntityProxy> extends IsWidget {
-  /**
-   * Implemented by the owner of a RecordTableView.
-   * 
-   * @param <R> the type of the records to display
-   */
-  interface Delegate<R extends EntityProxy> {
-    void createClicked();
-  }
-
-  HasData<P> asHasData();
-
-  /**
-   * @return the set of properties this view displays
-   */
-  String[] getPaths();
-
-  /**
-   * Sets the delegate.
-   */
-  void setDelegate(Delegate<P> delegate);
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyPlace.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyPlace.java
deleted file mode 100644
index fdb0af8..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyPlace.java
+++ /dev/null
@@ -1,155 +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.expenses.client.place;
-
-import com.google.gwt.place.shared.Place;
-import com.google.gwt.place.shared.PlaceTokenizer;
-import com.google.gwt.place.shared.Prefix;
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.EntityProxyId;
-import com.google.gwt.requestfactory.shared.RequestFactory;
-
-/**
- * A place in the app that deals with a specific {@link RequestFactory} proxy.
- */
-public class ProxyPlace extends Place {
-  /**
-   * The things you do with a record, each of which is a different bookmarkable
-   * location in the scaffold app.
-   */
-  public enum Operation {
-    CREATE, EDIT, DETAILS
-  }
-
-  /**
-   * Tokenizer.
-   */
-  @Prefix("r")
-  public static class Tokenizer implements PlaceTokenizer<ProxyPlace> {
-    /**
-     * 
-     */
-    private static final String SEPARATOR = "!";
-    private final RequestFactory requests;
-
-    public Tokenizer(RequestFactory requests) {
-      this.requests = requests;
-    }
-
-    public ProxyPlace getPlace(String token) {
-      String bits[] = token.split(SEPARATOR);
-      Operation operation = Operation.valueOf(bits[1]);
-      if (Operation.CREATE == operation) {
-        return new ProxyPlace(requests.getProxyClass(bits[0]));
-      }
-      return new ProxyPlace(requests.getProxyId(bits[0]), operation);
-    }
-
-    public String getToken(ProxyPlace place) {
-      if (Operation.CREATE == place.getOperation()) {
-        return requests.getHistoryToken(place.getProxyClass()) + SEPARATOR
-            + place.getOperation();
-      }
-      return requests.getHistoryToken(place.getProxyId()) + SEPARATOR
-          + place.getOperation();
-    }
-  }
-
-  private final EntityProxyId<?> proxyId;
-  private final Class<? extends EntityProxy> proxyClass;
-  private final Operation operation;
-
-  public ProxyPlace(Class<? extends EntityProxy> proxyClass) {
-    this.operation = Operation.CREATE;
-    this.proxyId = null;
-    this.proxyClass = proxyClass;
-  }
-
-  public ProxyPlace(EntityProxyId<?> record) {
-    this(record, Operation.DETAILS);
-  }
-
-  public ProxyPlace(EntityProxyId<?> proxyId, Operation operation) {
-    this.operation = operation;
-    this.proxyId = proxyId;
-    this.proxyClass = null;
-    assert Operation.CREATE != operation;
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (this == obj) {
-      return true;
-    }
-    if (obj == null) {
-      return false;
-    }
-    if (getClass() != obj.getClass()) {
-      return false;
-    }
-    ProxyPlace other = (ProxyPlace) obj;
-    if (operation != other.operation) {
-      return false;
-    }
-    if (proxyClass == null) {
-      if (other.proxyClass != null) {
-        return false;
-      }
-    } else if (!proxyClass.equals(other.proxyClass)) {
-      return false;
-    }
-    if (proxyId == null) {
-      if (other.proxyId != null) {
-        return false;
-      }
-    } else if (!proxyId.equals(other.proxyId)) {
-      return false;
-    }
-    return true;
-  }
-
-  public Operation getOperation() {
-    return operation;
-  }
-
-  public Class<? extends EntityProxy> getProxyClass() {
-    return proxyId != null ? proxyId.getProxyClass() : proxyClass;
-  }
-
-  /**
-   * @return the proxyId, or null if the operation is {@link Operation#CREATE}
-   */
-  public EntityProxyId<?> getProxyId() {
-    return proxyId;
-  }
-
-  @Override
-  public int hashCode() {
-    final int prime = 31;
-    int result = 1;
-    result = prime * result + ((operation == null) ? 0 : operation.hashCode());
-    result = prime * result
-        + ((proxyClass == null) ? 0 : proxyClass.hashCode());
-    result = prime * result + ((proxyId == null) ? 0 : proxyId.hashCode());
-    return result;
-  }
-
-  @Override
-  public String toString() {
-    return "ProxyPlace [operation=" + operation + ", proxy=" + proxyId
-        + ", proxyClass=" + proxyClass + "]";
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyPlaceToListPlace.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyPlaceToListPlace.java
deleted file mode 100644
index 7b15f3c..0000000
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ProxyPlaceToListPlace.java
+++ /dev/null
@@ -1,51 +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.expenses.client.place;
-
-import com.google.gwt.activity.shared.FilteredActivityMapper;
-import com.google.gwt.place.shared.Place;
-
-/**
- * Converts a {@link ProxyPlace} to a {@link ProxyListPlace}.
- */
-public class ProxyPlaceToListPlace implements FilteredActivityMapper.Filter {
-
-  /**
-   * Required by {@link com.google.gwt.app.place.FilteredActivityMapper.Filter}, calls
-   * {@link #proxyListPlaceFor}.
-   */
-  public Place filter(Place place) {
-    return proxyListPlaceFor(place);
-  }
-
-  /**
-   * @param place a place to process
-   * @return an appropriate ProxyListPlace, or null if the given place has
-   *         nothing to do with proxies
-   */
-  public ProxyListPlace proxyListPlaceFor(Place place) {
-    if (place instanceof ProxyListPlace) {
-      return (ProxyListPlace) place;
-    }
-
-    if (!(place instanceof ProxyPlace)) {
-      return null;
-    }
-
-    ProxyPlace proxyPlace = (ProxyPlace) place;
-    return new ProxyListPlace(proxyPlace.getProxyClass());
-  }
-}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ReportListPlace.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ReportListPlace.java
new file mode 100644
index 0000000..0f513c1
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ReportListPlace.java
@@ -0,0 +1,85 @@
+/*
+ * 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.expenses.client.place;
+
+import com.google.gwt.http.client.URL;
+import com.google.gwt.place.shared.Place;
+import com.google.gwt.place.shared.PlaceTokenizer;
+import com.google.gwt.place.shared.Prefix;
+import com.google.gwt.requestfactory.shared.EntityProxyId;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.sample.expenses.shared.EmployeeProxy;
+
+/**
+ * A place in the app that shows a list of reports.
+ */
+public class ReportListPlace extends Place {
+
+  /**
+   * Tokenizer.
+   */
+  @Prefix("l")
+  public static class Tokenizer implements PlaceTokenizer<ReportListPlace> {
+    static final String SEPARATOR = "!";
+    private final RequestFactory requests;
+
+    public Tokenizer(RequestFactory requests) {
+      this.requests = requests;
+    }
+
+    public ReportListPlace getPlace(String token) {
+      String bits[] = token.split(SEPARATOR);
+
+      if (bits.length != 3) {
+        return null;
+      }
+
+      String reporterIdToken = bits[0];
+      String search = bits[1];
+      int page = Integer.valueOf(bits[2]);
+
+      return new ReportListPlace(requests.<EmployeeProxy> getProxyId(reporterIdToken),
+          URL.decodePathSegment(search), page);
+    }
+
+    public String getToken(ReportListPlace place) {
+      return requests.getHistoryToken(place.getReporterId()) + SEPARATOR
+          + URL.encodePathSegment(place.getSearch());
+    }
+  }
+
+  private final int page;
+  private final EntityProxyId<EmployeeProxy> reporterId;
+  private final String search;
+
+  public ReportListPlace(EntityProxyId<EmployeeProxy> reporter, String search, int page) {
+    this.reporterId = reporter;
+    this.search = search;
+    this.page = page;
+  }
+
+  public int getPage() {
+    return page;
+  }
+  
+  public EntityProxyId<EmployeeProxy> getReporterId() {
+    return reporterId;
+  }
+
+  public String getSearch() {
+    return search;
+  }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ReportPlace.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ReportPlace.java
new file mode 100644
index 0000000..a8c42f8
--- /dev/null
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/place/ReportPlace.java
@@ -0,0 +1,83 @@
+/*
+ * 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.expenses.client.place;
+
+import com.google.gwt.place.shared.Place;
+import com.google.gwt.place.shared.PlaceTokenizer;
+import com.google.gwt.place.shared.Prefix;
+import com.google.gwt.requestfactory.shared.EntityProxyId;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.sample.expenses.shared.ReportProxy;
+
+/**
+ * A place in the app that shows a list of reports.
+ */
+public class ReportPlace extends Place {
+
+  /**
+   * Tokenizer.
+   */
+  @Prefix("r")
+  public static class Tokenizer implements PlaceTokenizer<ReportPlace> {
+    private final RequestFactory requests;
+    private final ReportListPlace.Tokenizer listTokenizer;
+
+    public Tokenizer(ReportListPlace.Tokenizer listTokenizer,
+        RequestFactory requests) {
+      this.requests = requests;
+      this.listTokenizer = listTokenizer;
+    }
+
+    private static final String SEPARATOR = ReportListPlace.Tokenizer.SEPARATOR
+        + ReportListPlace.Tokenizer.SEPARATOR;
+
+    public ReportPlace getPlace(String token) {
+      String[] bits = token.split(SEPARATOR);
+      if (bits.length != 2) {
+        return null;
+      }
+
+      String listPlaceToken = bits[0];
+      String reporterToken = bits[1];
+      
+      return new ReportPlace(listTokenizer.getPlace(listPlaceToken),
+          requests.<ReportProxy> getProxyId(reporterToken));
+    }
+
+    public String getToken(ReportPlace place) {
+      return listTokenizer.getToken(place.getListPlace()) + SEPARATOR
+          + requests.getHistoryToken(place.getReportId());
+    }
+  }
+
+  private final ReportListPlace listPlace;
+
+  private final EntityProxyId<ReportProxy> reportId;
+
+  public ReportPlace(ReportListPlace listPlace,
+      EntityProxyId<ReportProxy> reportId) {
+    this.listPlace = listPlace;
+    this.reportId = reportId;
+  }
+
+  public ReportListPlace getListPlace() {
+    return listPlace;
+  }
+
+  public EntityProxyId<ReportProxy> getReportId() {
+    return reportId;
+  }
+}
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/EmployeeProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java
similarity index 96%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/EmployeeProxy.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java
index 8cb3a09..fffc9f1 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/EmployeeProxy.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeProxy.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/EmployeeRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java
similarity index 97%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/EmployeeRequest.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java
index 8fe44d8..0391a48 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/EmployeeRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/EmployeeRequest.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.InstanceRequest;
 import com.google.gwt.requestfactory.shared.Request;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java
similarity index 96%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseProxy.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java
index cd72cf3..eabf78e 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseProxy.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseProxy.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java
similarity index 96%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java
index e7b1b8d..38a279c 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpenseRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpenseRequest.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.InstanceRequest;
 import com.google.gwt.requestfactory.shared.Request;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpensesEntityTypesProcessor.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java
similarity index 98%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpensesEntityTypesProcessor.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java
index 081bb60..23b7003 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpensesEntityTypesProcessor.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesEntityTypesProcessor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.EntityProxy;
 
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpensesRequestFactory.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java
similarity index 96%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpensesRequestFactory.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java
index d31bb6c..201abcb 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ExpensesRequestFactory.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ExpensesRequestFactory.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.UserInformationRequest;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ReportProxy.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java
similarity index 96%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ReportProxy.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java
index e048609..0bf0c48 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ReportProxy.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportProxy.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ReportRequest.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java
similarity index 97%
rename from samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ReportRequest.java
rename to samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java
index 160f277..40af51e 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/request/ReportRequest.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/shared/ReportRequest.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.sample.expenses.client.request;
+package com.google.gwt.sample.expenses.shared;
 
 import com.google.gwt.requestfactory.shared.InstanceRequest;
 import com.google.gwt.requestfactory.shared.Request;