Change Activity to an abstract class, to allow its api to evolve.

Gwt issue http://code.google.com/p/google-web-toolkit/issues/detail?id=5730

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

Review by: robertvawter@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9384 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
index d4c02c7..6ab26c8 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportDetails.java
@@ -16,6 +16,8 @@
 package com.google.gwt.sample.expenses.client;
 
 import com.google.gwt.activity.shared.Activity;
+import com.google.gwt.activity.shared.IsActivity;
+import com.google.gwt.activity.shared.SimpleActivity;
 import com.google.gwt.cell.client.AbstractInputCell;
 import com.google.gwt.cell.client.Cell;
 import com.google.gwt.cell.client.DateCell;
@@ -23,7 +25,6 @@
 import com.google.gwt.cell.client.NumberCell;
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.cell.client.ValueUpdater;
-import com.google.gwt.cell.client.Cell.Context;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
@@ -47,6 +48,7 @@
 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;
@@ -89,11 +91,11 @@
  * Details about the current expense report on the right side of the app,
  * including the list of expenses.
  */
-public class ExpenseReportDetails extends Composite implements Activity {
+public class ExpenseReportDetails extends Composite implements IsActivity {
 
   interface Binder extends UiBinder<Widget, ExpenseReportDetails> {
   }
-
+  
   /**
    * Fetches an employee and a report in parallel. A fine example of the kind of
    * thing that will no longer be necessary when RequestFactory provides server
@@ -127,7 +129,7 @@
       });
     }
   }
-
+  
   /**
    * The resources applied to the table.
    */
@@ -323,6 +325,13 @@
     }
   }
 
+  private final Activity activityAspect = new SimpleActivity() {
+    @Override
+    public void start(AcceptsOneWidget panel, EventBus eventBus) {
+      ExpenseReportDetails.this.start(panel, eventBus);
+    }
+  };
+
   private static Template template;
 
   /**
@@ -469,6 +478,10 @@
     });
   }
 
+  public Activity asActivity() {
+    return activityAspect;
+  }
+
   public ReportListPlace getReportListPlace() {
     ReportListPlace listPlace = place.getListPlace();
     return listPlace == null ? ReportListPlace.ALL : listPlace;
@@ -478,13 +491,6 @@
     return reportsLink;
   }
 
-  public String mayStop() {
-    return null;
-  }
-
-  public void onCancel() {
-  }
-
   public void onExpenseRecordChanged(EntityProxyChange<ExpenseProxy> event) {
     final EntityProxyId<ExpenseProxy> proxyId = event.getProxyId();
 
@@ -531,10 +537,17 @@
     }
   }
 
-  public void onStop() {
+  /**
+   * In this application, called by {@link ExpensesActivityMapper} each time a
+   * ReportListPlace is posted. In a more typical set up, this would be a
+   * constructor argument to a one shot activity, perhaps managing a shared
+   * widget view instance.
+   */
+  public void updateForPlace(final ReportPlace place) {
+    this.place = place;
   }
 
-  public void start(AcceptsOneWidget panel, EventBus eventBus) {
+  void start(AcceptsOneWidget panel, EventBus eventBus) {
     final ReportListPlace listPlace = place.getListPlace();
 
     if (listPlace.getEmployeeId() == null) {
@@ -575,16 +588,6 @@
   }
 
   /**
-   * In this application, called by {@link ExpensesActivityMapper} each time a
-   * ReportListPlace is posted. In a more typical set up, this would be a
-   * constructor argument to a one shot activity, perhaps managing a shared
-   * widget view instance.
-   */
-  public void updateForPlace(final ReportPlace place) {
-    this.place = place;
-  }
-
-  /**
    * Add a column of a {@link Comparable} type using default comparators.
    * 
    * @param <C> the column type
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
index e751bf0..e970c24 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpenseReportList.java
@@ -16,6 +16,8 @@
 package com.google.gwt.sample.expenses.client;
 
 import com.google.gwt.activity.shared.Activity;
+import com.google.gwt.activity.shared.IsActivity;
+import com.google.gwt.activity.shared.SimpleActivity;
 import com.google.gwt.cell.client.AbstractCell;
 import com.google.gwt.cell.client.Cell;
 import com.google.gwt.cell.client.DateCell;
@@ -76,7 +78,7 @@
  * The list of expense reports on the right side of the app.
  */
 public class ExpenseReportList extends Composite implements
-    EntityProxyChange.Handler<ReportProxy>, Activity {
+    EntityProxyChange.Handler<ReportProxy>, IsActivity {
 
   interface Binder extends UiBinder<Widget, ExpenseReportList> {
   }
@@ -177,6 +179,23 @@
     }
   }
 
+  private final Activity activityAspect = new SimpleActivity() {
+    @Override
+    public void onCancel() {
+      ExpenseReportList.this.onCancel();
+    }
+
+    @Override
+    public void onStop() {
+      ExpenseReportList.this.onStop();
+    }
+
+    @Override
+    public void start(AcceptsOneWidget panel, EventBus eventBus) {
+      ExpenseReportList.this.start(panel, eventBus);
+    }
+  };
+
   private static final ProvidesKey<ReportProxy> keyProvider = new EntityProxyKeyProvider<ReportProxy>();
 
   /**
@@ -337,12 +356,8 @@
     });
   }
 
-  public String mayStop() {
-    return null;
-  }
-
-  public void onCancel() {
-    onStop();
+  public Activity asActivity() {
+    return activityAspect;
   }
 
   public void onProxyChange(EntityProxyChange<ReportProxy> event) {
@@ -359,24 +374,10 @@
     }
   }
 
-  public void onStop() {
-    running = false;
-    refreshTimer.cancel();
-  }
-
   public void setListener(Listener listener) {
     this.listener = listener;
   }
 
-  public void start(AcceptsOneWidget panel, EventBus eventBus) {
-    running = true;
-    doUpdateForPlace();
-
-    EntityProxyChange.registerForProxyType(eventBus, ReportProxy.class, this);
-    requestReports(false);
-    panel.setWidget(this);
-  }
-
   /**
    * In this application, called by {@link ExpensesActivityMapper} each time a
    * ReportListPlace is posted. In a more typical set up, this would be a
@@ -398,6 +399,24 @@
     return p;
   }
 
+  void onCancel() {
+    onStop();
+  }
+
+  void onStop() {
+    running = false;
+    refreshTimer.cancel();
+  }
+
+  void start(AcceptsOneWidget panel, EventBus eventBus) {
+    running = true;
+    doUpdateForPlace();
+
+    EntityProxyChange.registerForProxyType(eventBus, ReportProxy.class, this);
+    requestReports(false);
+    panel.setWidget(this);
+  }
+
   /**
    * Add a sortable column to the table.
    * 
diff --git a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java
index ed58976..c50f736 100644
--- a/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java
+++ b/samples/expenses/src/main/java/com/google/gwt/sample/expenses/client/ExpensesActivityMapper.java
@@ -38,12 +38,12 @@
   public Activity getActivity(Place place) {
     if (place instanceof ReportListPlace) {
       expenseList.updateForPlace((ReportListPlace) place);
-      return expenseList;
+      return expenseList.asActivity();
     }
 
     if (place instanceof ReportPlace) {
       expenseDetails.updateForPlace((ReportPlace) place);
-      return expenseDetails;
+      return expenseDetails.asActivity();
     }
 
     return null;
diff --git a/samples/expenses/src/main/resources/log4j.properties b/samples/expenses/src/main/resources/log4j.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samples/expenses/src/main/resources/log4j.properties
diff --git a/tools/api-checker/config/gwt21_22userApi.conf b/tools/api-checker/config/gwt21_22userApi.conf
index 37f4e11..5c5ccf2 100644
--- a/tools/api-checker/config/gwt21_22userApi.conf
+++ b/tools/api-checker/config/gwt21_22userApi.conf
@@ -113,6 +113,10 @@
 # when adding to the white-list, include comments as to why the addition is
 # being made.
 
+# Changes to make Activity api evolvable in 2.1.1
+com.google.gwt.activity.shared.AbstractActivity MISSING
+com.google.gwt.activity.shared.Activity STATIC_REMOVED
+
 # RequestFactory tweaks in 2.1.1
 com.google.gwt.requestfactory.client.DefaultRequestTransport::DefaultRequestTransport(Lcom/google/gwt/event/shared/EventBus;) MISSING
 com.google.gwt.requestfactory.shared.RequestEvent MISSING
diff --git a/user/src/com/google/gwt/activity/shared/Activity.java b/user/src/com/google/gwt/activity/shared/Activity.java
index f141171..209f350 100644
--- a/user/src/com/google/gwt/activity/shared/Activity.java
+++ b/user/src/com/google/gwt/activity/shared/Activity.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
@@ -19,45 +19,51 @@
 import com.google.gwt.user.client.ui.AcceptsOneWidget;
 
 /**
- * Implemented by objects that control a piece of user interface, with a life
- * cycle managed by an {@link ActivityManager}, in response to
- * {@link com.google.gwt.place.shared.PlaceChangeEvent} events as the user
- * navigates through the app.
+ * Object that controls a piece of user interface, with a life cycle managed by
+ * an {@link ActivityManager}.
+ * <p>
+ * Ideally this would be an interface rather than an abstract class, but we
+ * expect its api will need to evolve (slightly) in the near term. When it
+ * settles down, an interface may be introduced. To this end, future versions of
+ * this class should not introduce non-trivial behavior.
+ * <p>
+ * For composition, see {@link IsActivity}.
  */
-public interface Activity {
+public abstract class Activity {
+
   /**
    * Called when the user is trying to navigate away from this activity.
-   *
+   * 
    * @return A message to display to the user, e.g. to warn of unsaved work, or
    *         null to say nothing
    */
-  String mayStop();
+  public abstract String mayStop();
 
   /**
    * Called when {@link #start} has not yet replied to its callback, but the
    * user has lost interest.
    */
-  void onCancel();
+  public abstract void onCancel();
 
   /**
    * Called when the Activity's widget has been removed from view. All event
    * handlers it registered will have been removed before this method is called.
    */
-  void onStop();
+  public abstract void onStop();
 
   /**
    * Called when the Activity should ready its widget for the user. When the
    * widget is ready (typically after an RPC response has been received),
    * receiver should present it by calling
-   * {@link AcceptsOneWidget#setWidget(IsWidget)} on the given panel.
+   * {@link AcceptsOneWidget#setWidget()} on the given panel.
    * <p>
    * Any handlers attached to the provided event bus will be de-registered when
    * the activity is stopped, so activities will rarely need to hold on to the
    * {@link com.google.gwt.event.shared.HandlerRegistration HandlerRegistration}
    * instances returned by {@link EventBus#addHandler}.
-   *
+   * 
    * @param panel the panel to display this activity's widget when it is ready
    * @param eventBus the event bus
    */
-  void start(AcceptsOneWidget panel, EventBus eventBus);
+  public abstract void start(AcceptsOneWidget panel, EventBus eventBus);
 }
diff --git a/user/src/com/google/gwt/activity/shared/ActivityManager.java b/user/src/com/google/gwt/activity/shared/ActivityManager.java
index 45f41b5..532e5d0 100644
--- a/user/src/com/google/gwt/activity/shared/ActivityManager.java
+++ b/user/src/com/google/gwt/activity/shared/ActivityManager.java
@@ -54,7 +54,7 @@
     }
   }
 
-  private static final Activity NULL_ACTIVITY = new AbstractActivity() {
+  private static final Activity NULL_ACTIVITY = new SimpleActivity() {
     public void start(AcceptsOneWidget panel, EventBus eventBus) {
     }
   };
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/user/src/com/google/gwt/activity/shared/IsActivity.java
similarity index 69%
copy from user/src/com/google/gwt/activity/shared/AbstractActivity.java
copy to user/src/com/google/gwt/activity/shared/IsActivity.java
index 59e596c..bc06054 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/user/src/com/google/gwt/activity/shared/IsActivity.java
@@ -15,19 +15,14 @@
  */
 package com.google.gwt.activity.shared;
 
+
 /**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
+ * Implemented by objects that can return an {@link Activity} aspect.
  */
-public abstract class AbstractActivity implements Activity {
-
-  public String mayStop() {
-    return null;
-  }
-
-  public void onCancel() {
-  }
-
-  public void onStop() {
-  }
+public interface IsActivity {
+  
+  /**
+   * Return the {@link Activity} aspect of this object.
+   */
+  Activity asActivity();
 }
diff --git a/user/src/com/google/gwt/activity/shared/AbstractActivity.java b/user/src/com/google/gwt/activity/shared/SimpleActivity.java
similarity index 79%
rename from user/src/com/google/gwt/activity/shared/AbstractActivity.java
rename to user/src/com/google/gwt/activity/shared/SimpleActivity.java
index 59e596c..8ed2ed4 100644
--- a/user/src/com/google/gwt/activity/shared/AbstractActivity.java
+++ b/user/src/com/google/gwt/activity/shared/SimpleActivity.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2010 Google Inc.
- *
+ * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- *
+ * 
  * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -15,19 +15,28 @@
  */
 package com.google.gwt.activity.shared;
 
-/**
- * Simple Activity implementation that is always willing to stop,
- * and does nothing onStop and onCancel.
- */
-public abstract class AbstractActivity implements Activity {
 
+/**
+ * Simple base implementation of {@link Activity}.
+ */
+public abstract class SimpleActivity extends Activity {
+
+  /**
+   * Return null.
+   */
   public String mayStop() {
     return null;
   }
 
+  /**
+   * No-op.
+   */
   public void onCancel() {
   }
 
+  /**
+   * No-op.
+   */
   public void onStop() {
   }
 }
diff --git a/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java b/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
index 0f25f8b..16de717 100644
--- a/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
+++ b/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
@@ -81,7 +81,7 @@
       return null;
     }
   }
-  private static class SyncActivity implements Activity {
+  private static class SyncActivity extends Activity {
     boolean canceled = false;
     boolean stopped = false;
     AcceptsOneWidget display;