diff --git a/README.md b/README.md index 4aa9bd7..8a709ad 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,47 @@ [![Build Status](https://travis-ci.org/gwtplus/gwt-activity.svg?branch=master)](https://travis-ci.org/gwtplus/gwt-activity) [![jitpack.io](https://jitpack.io/v/gwtplus/gwt-activity.svg)](https://jitpack.io/#gwtplus/gwt-activity) -Preparing for GWT 3 and j2cl \ No newline at end of file +A future-proof port of the `com.google.gwt.activity.Activity` GWT module, with no dependency on `gwt-user` (besides the Java Runtime Emulation), to prepare for GWT 3 / J2Cl. + +## Migrating from `com.google.gwt.activity.Activity` + +1. Add the dependency to your build. + + For Maven: + + ```xml + + org.gwtproject.activity + gwt-activity + HEAD-SNAPSHOT + + ``` + + For Gradle: + + ```gradle + implementation("org.gwtproject.activity:gwt-activity:HEAD-SNAPSHOT") + ``` + +1. Update your GWT module to use + + ```xml + + ``` + +1. Change the `import`s in your Java source files: + + ```java + import org.gwtproject.activity.*; + ``` + +1. Provide implementation of `ActivityDisplay` or use any of provided ones (`WidgetActivityDisplay` from `WidgetActivity` module or `ElementalActivityDisplay` from `ElementalActivity`. Note that `ActivityDisplay` uses `show()` instead od `setWidget()`. + +1. Specialize other `org.gwtproject.activity.*` references to `` + + +## Dependencies + +GWT Activity depends on the following modules: +* gwt-places +* gwt-event diff --git a/pom.xml b/pom.xml index dc9e34d..45ec8d4 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ UTF-8 1.8 - 2.8.2 + 2.9.0 @@ -66,6 +66,20 @@ HEAD-SNAPSHOT + + com.google.elemental2 + elemental2-dom + 1.1.0 + true + + + + org.gwtproject.widgets + gwt-widgets + 1.0-SNAPSHOT + true + + junit @@ -177,6 +191,7 @@ org/gwtproject/activity/ActivityJreSuite.java + org/gwtproject/activity/widget/WidgetActivityJreSuite.java diff --git a/src/main/java/org/gwtproject/user/package-info.java b/src/main/java/org/gwtproject/activity/elemental/package-info.java similarity index 81% rename from src/main/java/org/gwtproject/user/package-info.java rename to src/main/java/org/gwtproject/activity/elemental/package-info.java index 6dac726..4cf9320 100644 --- a/src/main/java/org/gwtproject/user/package-info.java +++ b/src/main/java/org/gwtproject/activity/elemental/package-info.java @@ -15,8 +15,6 @@ */ /** - * Temporary port until proper dependency is not available as snapshot - * - * FIXME: remove whole package + * Classes used to implement app navigation using elemental. */ -package org.gwtproject.user; \ No newline at end of file +package org.gwtproject.activity.elemental; \ No newline at end of file diff --git a/src/main/java/org/gwtproject/activity/elemental/shared/ElementalActivityDisplay.java b/src/main/java/org/gwtproject/activity/elemental/shared/ElementalActivityDisplay.java new file mode 100644 index 0000000..132dc16 --- /dev/null +++ b/src/main/java/org/gwtproject/activity/elemental/shared/ElementalActivityDisplay.java @@ -0,0 +1,53 @@ +/* + * 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 org.gwtproject.activity.elemental.shared; + +import org.gwtproject.activity.shared.ActivityDisplay; + +import elemental2.dom.HTMLElement; + +/** + * {@link ActivityDisplay} which wraps {@link HTMLElement} to provide + * display for {@link HTMLElement} views. + */ +public class ElementalActivityDisplay implements ActivityDisplay { + + public ElementalActivityDisplay(HTMLElement delegate) { + this.delegate = delegate; + } + + public void show(HTMLElement view) { + + if (this.view == view) { + return; + } + + while (delegate.firstChild != null) { + delegate.removeChild(delegate.firstChild); + } + + if (view != null) { + delegate.appendChild(view); + } + + this.view = view; + } + + private final HTMLElement delegate; + + private HTMLElement view; + +} \ No newline at end of file diff --git a/src/main/java/org/gwtproject/activity/shared/AbstractActivity.java b/src/main/java/org/gwtproject/activity/shared/AbstractActivity.java index 9ba6fe9..3381668 100644 --- a/src/main/java/org/gwtproject/activity/shared/AbstractActivity.java +++ b/src/main/java/org/gwtproject/activity/shared/AbstractActivity.java @@ -18,8 +18,10 @@ /** * Simple Activity implementation that is always willing to stop, and does * nothing onStop and onCancel. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) */ -public abstract class AbstractActivity implements Activity { +public abstract class AbstractActivity implements Activity { public String mayStop() { return null; diff --git a/src/main/java/org/gwtproject/activity/shared/Activity.java b/src/main/java/org/gwtproject/activity/shared/Activity.java index 0d7bfb9..26cfab3 100644 --- a/src/main/java/org/gwtproject/activity/shared/Activity.java +++ b/src/main/java/org/gwtproject/activity/shared/Activity.java @@ -16,15 +16,16 @@ package org.gwtproject.activity.shared; import org.gwtproject.event.shared.EventBus; -import org.gwtproject.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 org.gwtproject.place.shared.PlaceChangeEvent} events as the user * navigates through the app. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) */ -public interface Activity { +public interface Activity { /** * Called when the user is trying to navigate away from this activity. @@ -50,7 +51,7 @@ public interface Activity { * 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} on the given panel. + * {@link ActivityDisplay#show} on the given panel. *

* 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 @@ -60,5 +61,5 @@ public interface Activity { * @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); + void start(ActivityDisplay panel, EventBus eventBus); } diff --git a/src/main/java/org/gwtproject/activity/shared/ActivityDisplay.java b/src/main/java/org/gwtproject/activity/shared/ActivityDisplay.java new file mode 100644 index 0000000..cbfc827 --- /dev/null +++ b/src/main/java/org/gwtproject/activity/shared/ActivityDisplay.java @@ -0,0 +1,36 @@ +/* + * 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 org.gwtproject.activity.shared; + +import org.gwtproject.activity.widget.shared.WidgetActivityDisplay; + +/** Display used to show views of type {@code V}. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) + */ +public interface ActivityDisplay { + + /** + * Set the element to display, replacing the previously displayed + * element if there was one. + * + * @param view object to display, or null to clear display + * + * @see WidgetActivityDisplay + */ + void show(V view); + +} diff --git a/src/main/java/org/gwtproject/activity/shared/ActivityManager.java b/src/main/java/org/gwtproject/activity/shared/ActivityManager.java index faba5f5..8e6104c 100644 --- a/src/main/java/org/gwtproject/activity/shared/ActivityManager.java +++ b/src/main/java/org/gwtproject/activity/shared/ActivityManager.java @@ -21,8 +21,6 @@ import org.gwtproject.event.shared.UmbrellaException; import org.gwtproject.place.shared.PlaceChangeEvent; import org.gwtproject.place.shared.PlaceChangeRequestEvent; -import org.gwtproject.user.client.ui.AcceptsOneWidget; -import org.gwtproject.user.client.ui.IsWidget; import java.util.LinkedHashSet; import java.util.Set; @@ -31,21 +29,24 @@ * Manages {@link Activity} objects that should be kicked off in response to * {@link PlaceChangeEvent} events. Each activity can start itself * asynchronously, and provides a widget to be shown when it's ready to run. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) */ -public class ActivityManager implements PlaceChangeEvent.Handler, PlaceChangeRequestEvent.Handler { +public class ActivityManager implements PlaceChangeEvent.Handler, + PlaceChangeRequestEvent.Handler { /** * Wraps our real display to prevent an Activity from taking it over if it is * not the currentActivity. */ - private class ProtectedDisplay implements AcceptsOneWidget { - private final Activity activity; + private class ProtectedDisplay implements ActivityDisplay { + private final Activity activity; - ProtectedDisplay(Activity activity) { + ProtectedDisplay(Activity activity) { this.activity = activity; } - public void setWidget(IsWidget view) { + public void show(V view) { if (this.activity == ActivityManager.this.currentActivity) { startingNext = false; showWidget(view); @@ -53,12 +54,12 @@ public void setWidget(IsWidget view) { } } - private static final Activity NULL_ACTIVITY = new AbstractActivity() { - public void start(AcceptsOneWidget panel, EventBus eventBus) { + private final Activity nullActivity = new AbstractActivity() { + public void start(ActivityDisplay panel, EventBus eventBus) { } }; - private final ActivityMapper mapper; + private final ActivityMapper mapper; private final EventBus eventBus; @@ -68,9 +69,9 @@ public void start(AcceptsOneWidget panel, EventBus eventBus) { */ private final ResettableEventBus stopperedEventBus; - private Activity currentActivity = NULL_ACTIVITY; + private Activity currentActivity = nullActivity; - private AcceptsOneWidget display; + private ActivityDisplay display; private boolean startingNext = false; @@ -84,7 +85,7 @@ public void start(AcceptsOneWidget panel, EventBus eventBus) { * @param eventBus source of {@link PlaceChangeEvent} and * {@link PlaceChangeRequestEvent} events. */ - public ActivityManager(ActivityMapper mapper, EventBus eventBus) { + public ActivityManager(ActivityMapper mapper, EventBus eventBus) { this.mapper = mapper; this.eventBus = eventBus; this.stopperedEventBus = new ResettableEventBus(eventBus); @@ -113,14 +114,14 @@ public EventBus getActiveEventBus() { * treatment. */ public void onPlaceChange(PlaceChangeEvent event) { - Activity nextActivity = getNextActivity(event); + Activity nextActivity = getNextActivity(event); Throwable caughtOnStop = null; Throwable caughtOnCancel = null; Throwable caughtOnStart = null; if (nextActivity == null) { - nextActivity = NULL_ACTIVITY; + nextActivity = nullActivity; } if (currentActivity.equals(nextActivity)) { @@ -131,9 +132,9 @@ public void onPlaceChange(PlaceChangeEvent event) { // The place changed again before the new current activity showed its // widget caughtOnCancel = tryStopOrCancel(false); - currentActivity = NULL_ACTIVITY; + currentActivity = nullActivity; startingNext = false; - } else if (!currentActivity.equals(NULL_ACTIVITY)) { + } else if (!currentActivity.equals(nullActivity)) { showWidget(null); /* @@ -146,7 +147,7 @@ public void onPlaceChange(PlaceChangeEvent event) { currentActivity = nextActivity; - if (currentActivity.equals(NULL_ACTIVITY)) { + if (currentActivity.equals(nullActivity)) { showWidget(null); } else { startingNext = true; @@ -189,7 +190,7 @@ public void onPlaceChangeRequest(PlaceChangeRequestEvent event) { * * @param display an instance of AcceptsOneWidget */ - public void setDisplay(AcceptsOneWidget display) { + public void setDisplay(ActivityDisplay display) { boolean wasActive = (null != this.display); boolean willBeActive = (null != display); this.display = display; @@ -198,7 +199,7 @@ public void setDisplay(AcceptsOneWidget display) { } } - private Activity getNextActivity(PlaceChangeEvent event) { + private Activity getNextActivity(PlaceChangeEvent event) { if (display == null) { /* * Display may have been nulled during PlaceChangeEvent dispatch. Don't @@ -210,9 +211,9 @@ private Activity getNextActivity(PlaceChangeEvent event) { return mapper.getActivity(event.getNewPlace()); } - private void showWidget(IsWidget view) { + private void showWidget(V view) { if (display != null) { - display.setWidget(view); + display.show(view); } } diff --git a/src/main/java/org/gwtproject/activity/shared/ActivityMapper.java b/src/main/java/org/gwtproject/activity/shared/ActivityMapper.java index 2a37136..5a10c30 100644 --- a/src/main/java/org/gwtproject/activity/shared/ActivityMapper.java +++ b/src/main/java/org/gwtproject/activity/shared/ActivityMapper.java @@ -20,12 +20,14 @@ /** * Finds the activity to run for a given {@link Place}, used to configure * an {@link ActivityManager}. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) */ -public interface ActivityMapper { +public interface ActivityMapper { /** * Returns the activity to run for the given {@link Place}, or null. * * @param place a Place object */ - Activity getActivity(Place place); + Activity getActivity(Place place); } diff --git a/src/main/java/org/gwtproject/activity/shared/CachingActivityMapper.java b/src/main/java/org/gwtproject/activity/shared/CachingActivityMapper.java index 2a6c0de..49885ac 100644 --- a/src/main/java/org/gwtproject/activity/shared/CachingActivityMapper.java +++ b/src/main/java/org/gwtproject/activity/shared/CachingActivityMapper.java @@ -20,24 +20,26 @@ /** * Wraps another {@link ActivityMapper} and caches the last activity it * returned, to be re-used if we see the same place twice. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) */ -public class CachingActivityMapper implements ActivityMapper { +public class CachingActivityMapper implements ActivityMapper { - private final ActivityMapper wrapped; + private final ActivityMapper wrapped; private Place lastPlace; - private Activity lastActivity; + private Activity lastActivity; /** * Constructs a CachingActivityMapper object. * * @param wrapped an ActivityMapper object */ - public CachingActivityMapper(ActivityMapper wrapped) { + public CachingActivityMapper(ActivityMapper wrapped) { this.wrapped = wrapped; } - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { if (!place.equals(lastPlace)) { lastPlace = place; lastActivity = wrapped.getActivity(place); diff --git a/src/main/java/org/gwtproject/activity/shared/FilteredActivityMapper.java b/src/main/java/org/gwtproject/activity/shared/FilteredActivityMapper.java index 1e50190..7c2a28c 100644 --- a/src/main/java/org/gwtproject/activity/shared/FilteredActivityMapper.java +++ b/src/main/java/org/gwtproject/activity/shared/FilteredActivityMapper.java @@ -20,8 +20,10 @@ /** * Wraps an activity mapper and applies a filter to the place objects that it * sees. + * + * @param view type ({@code IsWidget}, {@code HTMLElement}, ...) */ -public class FilteredActivityMapper implements ActivityMapper { +public class FilteredActivityMapper implements ActivityMapper { /** * Implemented by objects that want to interpret one place as another. @@ -37,7 +39,7 @@ public interface Filter { } private final Filter filter; - private final ActivityMapper wrapped; + private final ActivityMapper wrapped; /** * Constructs a FilteredActivityMapper object. @@ -45,12 +47,12 @@ public interface Filter { * @param filter a Filter object * @param wrapped an ActivityMapper object */ - public FilteredActivityMapper(Filter filter, ActivityMapper wrapped) { + public FilteredActivityMapper(Filter filter, ActivityMapper wrapped) { this.filter = filter; this.wrapped = wrapped; } - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { return wrapped.getActivity(filter.filter(place)); } } diff --git a/src/main/java/org/gwtproject/activity/widget/package-info.java b/src/main/java/org/gwtproject/activity/widget/package-info.java new file mode 100644 index 0000000..e79c544 --- /dev/null +++ b/src/main/java/org/gwtproject/activity/widget/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Classes used to implement app navigation using widgets. + */ +package org.gwtproject.activity.widget; \ No newline at end of file diff --git a/src/main/java/org/gwtproject/user/client/ui/AcceptsOneWidget.java b/src/main/java/org/gwtproject/activity/widget/shared/AbstractWidgetActivity.java similarity index 58% rename from src/main/java/org/gwtproject/user/client/ui/AcceptsOneWidget.java rename to src/main/java/org/gwtproject/activity/widget/shared/AbstractWidgetActivity.java index f9a57a7..f3a6008 100644 --- a/src/main/java/org/gwtproject/user/client/ui/AcceptsOneWidget.java +++ b/src/main/java/org/gwtproject/activity/widget/shared/AbstractWidgetActivity.java @@ -1,34 +1,27 @@ /* * Copyright 2010 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ -package org.gwtproject.user.client.ui; +package org.gwtproject.activity.widget.shared; + +import org.gwtproject.activity.shared.AbstractActivity; +import org.gwtproject.user.client.ui.IsWidget; /** - * Implemented by displays that can be given accept an {@link IsWidget} - * to show. + * Drop-in replacement for {@link com.google.gwt.activity.shared.AbstractActivity}. */ -@FunctionalInterface -public interface AcceptsOneWidget { +public abstract class AbstractWidgetActivity extends AbstractActivity + implements WidgetActivity { - /** - * Set the only widget of the receiver, replacing the previous - * widget if there was one. - * - * @param w the widget, or null to remove the widget - * - * @see SimplePanel - */ - void setWidget(IsWidget w); } diff --git a/src/main/java/org/gwtproject/activity/widget/shared/CachingWidgetActivityMapper.java b/src/main/java/org/gwtproject/activity/widget/shared/CachingWidgetActivityMapper.java new file mode 100644 index 0000000..5cfa79a --- /dev/null +++ b/src/main/java/org/gwtproject/activity/widget/shared/CachingWidgetActivityMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.gwtproject.activity.widget.shared; + +import org.gwtproject.activity.shared.CachingActivityMapper; +import org.gwtproject.place.shared.Place; +import org.gwtproject.user.client.ui.IsWidget; + +/** + * Drop-in replacement for {@link com.google.gwt.activity.shared.CachingActivityMapper}. + */ +public class CachingWidgetActivityMapper extends CachingActivityMapper + implements WidgetActivityMapper { + + public CachingWidgetActivityMapper(WidgetActivityMapper wrapped) { + super(wrapped); + } + + @Override + public WidgetActivity getActivity(Place place) { + // we are wrapping WidgetActivityMapper, so it's safe to cast + return (WidgetActivity) super.getActivity(place); + } + +} diff --git a/src/main/java/org/gwtproject/activity/widget/shared/FilteredWidgetActivityMapper.java b/src/main/java/org/gwtproject/activity/widget/shared/FilteredWidgetActivityMapper.java new file mode 100644 index 0000000..81a0392 --- /dev/null +++ b/src/main/java/org/gwtproject/activity/widget/shared/FilteredWidgetActivityMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.gwtproject.activity.widget.shared; + +import org.gwtproject.activity.shared.FilteredActivityMapper; +import org.gwtproject.place.shared.Place; +import org.gwtproject.user.client.ui.IsWidget; + +/** + * Drop-in replacement for {@link com.google.gwt.activity.shared.FilteredActivityMapper}. + */ +public class FilteredWidgetActivityMapper extends FilteredActivityMapper + implements WidgetActivityMapper { + + public FilteredWidgetActivityMapper(Filter filter, WidgetActivityMapper wrapped) { + super(filter, wrapped); + } + + @Override + public WidgetActivity getActivity(Place place) { + // we are wrapping WidgetActivityMapper, so it's safe to cast + return (WidgetActivity) super.getActivity(place); + } + +} diff --git a/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivity.java b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivity.java new file mode 100644 index 0000000..4af9717 --- /dev/null +++ b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivity.java @@ -0,0 +1,35 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.gwtproject.activity.widget.shared; + +import org.gwtproject.activity.shared.Activity; +import org.gwtproject.activity.shared.ActivityDisplay; +import org.gwtproject.event.shared.EventBus; +import org.gwtproject.user.client.ui.AcceptsOneWidget; +import org.gwtproject.user.client.ui.IsWidget; + +/** + * Drop-in replacement for {@link com.google.gwt.activity.shared.Activity}. + */ +public interface WidgetActivity extends Activity { + + default void start(ActivityDisplay panel, EventBus eventBus) { + start((AcceptsOneWidget) panel::show, eventBus); + } + + void start(AcceptsOneWidget panel, EventBus eventBus); + +} diff --git a/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityDisplay.java b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityDisplay.java new file mode 100644 index 0000000..02cbf9e --- /dev/null +++ b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityDisplay.java @@ -0,0 +1,38 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.gwtproject.activity.widget.shared; + +import org.gwtproject.activity.shared.ActivityDisplay; +import org.gwtproject.user.client.ui.AcceptsOneWidget; +import org.gwtproject.user.client.ui.IsWidget; + +/** + * {@link ActivityDisplay} which wraps {@link AcceptsOneWidget} to provide + * display for {@link IsWidget} views. + */ +public class WidgetActivityDisplay implements ActivityDisplay { + + public WidgetActivityDisplay(AcceptsOneWidget delegate) { + this.delegate = delegate; + } + + public void show(IsWidget widget) { + delegate.setWidget(widget); + } + + private final AcceptsOneWidget delegate; + +} \ No newline at end of file diff --git a/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityManager.java b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityManager.java new file mode 100644 index 0000000..a7b231e --- /dev/null +++ b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityManager.java @@ -0,0 +1,89 @@ +/* + * 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 org.gwtproject.activity.widget.shared; + +import org.gwtproject.activity.shared.ActivityManager; +import org.gwtproject.event.shared.EventBus; +import org.gwtproject.place.shared.PlaceChangeEvent; +import org.gwtproject.place.shared.PlaceChangeRequestEvent; +import org.gwtproject.user.client.ui.AcceptsOneWidget; +import org.gwtproject.user.client.ui.IsWidget; + +/** + *

Drop-in replacement for {@link com.google.gwt.activity.shared.ActivityManager}.

+ * + *

Note: wraps {@link org.gwtproject.activity.shared.ActivityManager} rather than + * extending it which prevents casting between {@link ActivityManager} and + * {@link WidgetActivityManager}. Two alternatives exist:

+ * + *
    + *
  • access wrapped {@link ActivityManager} via {@link #getDelegateManager()} + *
  • provide your own {@link ActivityManager}-derived implementation + *
      + */ +public class WidgetActivityManager implements PlaceChangeEvent.Handler, + PlaceChangeRequestEvent.Handler { + + private final ActivityManager delegate; + + public WidgetActivityManager(WidgetActivityMapper mapper, EventBus eventBus) { + delegate = new ActivityManager<>(mapper, eventBus); + } + + @Override + public void onPlaceChangeRequest(PlaceChangeRequestEvent event) { + delegate.onPlaceChangeRequest(event); + } + + @Override + public void onPlaceChange(PlaceChangeEvent event) { + delegate.onPlaceChange(event); + } + + /** + * Returns wrapped {@link ActivityManager}. + */ + public ActivityManager getDelegateManager() { + return delegate; + } + + /** + * Returns an event bus which is in use by the currently running activity. + *

      + * Any handlers attached to the returned event bus will be de-registered when + * the current activity is stopped. + * + * @return the event bus used by the current activity + */ + public EventBus getActiveEventBus() { + return delegate.getActiveEventBus(); + } + + /** + * Sets the display for the receiver, and has the side effect of starting or + * stopping its monitoring the event bus for place change events. + *

      + * If you are disposing of an ActivityManager, it is important to call + * setDisplay(null) to get it to de-register from the event bus, so that it can + * be garbage collected. + * + * @param display an instance of AcceptsOneWidget + */ + public void setDisplay(AcceptsOneWidget display) { + delegate.setDisplay(display == null ? null : display::setWidget); + } + +} diff --git a/src/main/java/org/gwtproject/user/client/ui/IsWidget.java b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityMapper.java similarity index 57% rename from src/main/java/org/gwtproject/user/client/ui/IsWidget.java rename to src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityMapper.java index e0266ac..d56b9d8 100644 --- a/src/main/java/org/gwtproject/user/client/ui/IsWidget.java +++ b/src/main/java/org/gwtproject/activity/widget/shared/WidgetActivityMapper.java @@ -1,32 +1,30 @@ /* * 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 org.gwtproject.user.client.ui; +package org.gwtproject.activity.widget.shared; -import com.google.gwt.user.client.ui.Widget; +import org.gwtproject.activity.shared.ActivityMapper; +import org.gwtproject.place.shared.Place; +import org.gwtproject.user.client.ui.IsWidget; /** - * Extended by view interfaces that are likely to be implemented by Widgets. - * Provides access to that widget, if it exists, without compromising the - * ability to provide a mock view instance in JRE unit tests. + * Drop-in replacement for {@link com.google.gwt.activity.shared.ActivityMapper}. */ -@FunctionalInterface -public interface IsWidget { +public interface WidgetActivityMapper extends ActivityMapper { + + @Override + WidgetActivity getActivity(Place place); - /** - * Returns the {@link Widget} aspect of the receiver. - */ - Widget asWidget(); } diff --git a/src/main/resources/org/gwtproject/activity/Activity.gwt.xml b/src/main/resources/org/gwtproject/activity/Activity.gwt.xml index 919b4a2..0ac7086 100644 --- a/src/main/resources/org/gwtproject/activity/Activity.gwt.xml +++ b/src/main/resources/org/gwtproject/activity/Activity.gwt.xml @@ -3,7 +3,6 @@ - diff --git a/src/main/java/org/gwtproject/user/UI.gwt.xml b/src/main/resources/org/gwtproject/activity/elemental/ElementalActivity.gwt.xml similarity index 51% rename from src/main/java/org/gwtproject/user/UI.gwt.xml rename to src/main/resources/org/gwtproject/activity/elemental/ElementalActivity.gwt.xml index c9a1c8c..db40c21 100644 --- a/src/main/java/org/gwtproject/user/UI.gwt.xml +++ b/src/main/resources/org/gwtproject/activity/elemental/ElementalActivity.gwt.xml @@ -1,9 +1,8 @@ - - - + + - + diff --git a/src/main/resources/org/gwtproject/activity/widget/WidgetActivity.gwt.xml b/src/main/resources/org/gwtproject/activity/widget/WidgetActivity.gwt.xml new file mode 100644 index 0000000..cc8920c --- /dev/null +++ b/src/main/resources/org/gwtproject/activity/widget/WidgetActivity.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/test/java/org/gwtproject/activity/shared/ActivityManagerTest.java b/src/test/java/org/gwtproject/activity/shared/ActivityManagerTest.java index 7ab38e4..0de9ef6 100644 --- a/src/test/java/org/gwtproject/activity/shared/ActivityManagerTest.java +++ b/src/test/java/org/gwtproject/activity/shared/ActivityManagerTest.java @@ -22,10 +22,8 @@ import org.gwtproject.place.shared.Place; import org.gwtproject.place.shared.PlaceChangeEvent; import org.gwtproject.place.shared.PlaceChangeRequestEvent; -import org.gwtproject.user.client.ui.AcceptsOneWidget; import org.gwtproject.user.client.ui.IsWidget; - -import com.google.gwt.user.client.ui.Widget; +import org.gwtproject.user.client.ui.Widget; import junit.framework.TestCase; @@ -39,13 +37,13 @@ private static class AsyncActivity extends SyncActivity { } @Override - public void start(AcceptsOneWidget display, EventBus eventBus) { + public void start(ActivityDisplay display, EventBus eventBus) { this.display = display; this.bus = eventBus; } void finish() { - display.setWidget(view); + display.show(view); } } @@ -66,11 +64,11 @@ protected void dispatch(Handler handler) { private static class Handler { }; - private static class MyDisplay implements AcceptsOneWidget { + private static class MyDisplay implements ActivityDisplay { IsWidget view = null; @Override - public void setWidget(IsWidget view) { + public void show(IsWidget view) { this.view = view; } } @@ -84,10 +82,10 @@ public Widget asWidget() { return null; } } - private static class SyncActivity implements Activity { + private static class SyncActivity implements Activity { boolean canceled = false; boolean stopped = false; - AcceptsOneWidget display; + ActivityDisplay display; String stopWarning; MyView view; EventBus bus; @@ -112,10 +110,10 @@ public void onStop() { } @Override - public void start(AcceptsOneWidget display, EventBus eventBus) { + public void start(ActivityDisplay display, EventBus eventBus) { this.display = display; this.bus = eventBus; - display.setWidget(view); + display.show(view); } } @@ -127,9 +125,9 @@ public void start(AcceptsOneWidget display, EventBus eventBus) { private SyncActivity activity2 = new SyncActivity(new MyView()); private final MyDisplay realDisplay = new MyDisplay(); - private final ActivityMapper myMap = new ActivityMapper() { + private final ActivityMapper myMap = new ActivityMapper() { @Override - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { if (place.equals(place1)) { return activity1; } @@ -143,16 +141,16 @@ public Activity getActivity(Place place) { private CountingEventBus eventBus = new CountingEventBus(); - private ActivityManager manager = new ActivityManager( + private ActivityManager manager = new ActivityManager<>( myMap, eventBus); public void testActiveEventBus() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); - ActivityMapper map = new ActivityMapper() { + ActivityMapper map = new ActivityMapper() { @Override - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } @@ -164,7 +162,7 @@ public Activity getActivity(Place place) { } }; - manager = new ActivityManager(map, eventBus); + manager = new ActivityManager<>(map, eventBus); manager.setDisplay(realDisplay); eventBus.fireEvent(new PlaceChangeEvent(place1)); @@ -181,9 +179,9 @@ public void testAsyncDispatch() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); - ActivityMapper map = new ActivityMapper() { + ActivityMapper map = new ActivityMapper() { @Override - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } @@ -195,7 +193,7 @@ public Activity getActivity(Place place) { } }; - manager = new ActivityManager(map, eventBus); + manager = new ActivityManager<>(map, eventBus); manager.setDisplay(realDisplay); PlaceChangeRequestEvent event = new PlaceChangeRequestEvent(place1); @@ -243,9 +241,9 @@ public void testCancel() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity ayncActivity2 = new AsyncActivity(new MyView()); - ActivityMapper map = new ActivityMapper() { + ActivityMapper map = new ActivityMapper() { @Override - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } @@ -257,7 +255,7 @@ public Activity getActivity(Place place) { } }; - manager = new ActivityManager(map, eventBus); + manager = new ActivityManager<>(map, eventBus); manager.setDisplay(realDisplay); PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( @@ -304,7 +302,7 @@ public void testDropHandlersOnStop() { activity1 = new SyncActivity(null) { @Override - public void start(AcceptsOneWidget panel, EventBus eventBus) { + public void start(ActivityDisplay panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(MyEvent.TYPE, new Handler()); } @@ -347,7 +345,7 @@ public void testEventSetupAndTeardown() { public void testExceptionsOnStartAndCancel() { activity1 = new AsyncActivity(null) { @Override - public void start(AcceptsOneWidget panel, EventBus eventBus) { + public void start(ActivityDisplay panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(MyEvent.TYPE, new Handler()); } @@ -361,7 +359,7 @@ public void onCancel() { activity2 = new SyncActivity(null) { @Override - public void start(AcceptsOneWidget panel, EventBus eventBus) { + public void start(ActivityDisplay panel, EventBus eventBus) { super.start(panel, eventBus); throw new UnsupportedOperationException("Exception on start"); } @@ -394,7 +392,7 @@ public void start(AcceptsOneWidget panel, EventBus eventBus) { public void testExceptionsOnStopAndStart() { activity1 = new SyncActivity(null) { @Override - public void start(AcceptsOneWidget panel, EventBus eventBus) { + public void start(ActivityDisplay panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(MyEvent.TYPE, new Handler()); } @@ -408,7 +406,7 @@ public void onStop() { activity2 = new SyncActivity(null) { @Override - public void start(AcceptsOneWidget panel, EventBus eventBus) { + public void start(ActivityDisplay panel, EventBus eventBus) { super.start(panel, eventBus); throw new UnsupportedOperationException("Exception on start"); } @@ -464,9 +462,9 @@ public void testNullDisplayBeforeAsyncStart() { final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); - ActivityMapper map = new ActivityMapper() { + ActivityMapper map = new ActivityMapper() { @Override - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { if (place.equals(place1)) { return asyncActivity1; } @@ -478,7 +476,7 @@ public Activity getActivity(Place place) { } }; - manager = new ActivityManager(map, eventBus); + manager = new ActivityManager<>(map, eventBus); manager.setDisplay(realDisplay); // Start an activity @@ -557,23 +555,23 @@ public TwoViewActivity(MyView view1, MyView view2) { } void secondView() { - display.setWidget(view2); + display.show(view2); } void firstView() { - display.setWidget(view); + display.show(view); } } final TwoViewActivity activity = new TwoViewActivity(new MyView(), new MyView()); - ActivityMapper map = new ActivityMapper() { + ActivityMapper map = new ActivityMapper() { @Override - public Activity getActivity(Place place) { + public Activity getActivity(Place place) { return activity; } }; - manager = new ActivityManager(map, eventBus); + manager = new ActivityManager<>(map, eventBus); manager.setDisplay(realDisplay); // Start an activity diff --git a/src/test/java/org/gwtproject/activity/widget/WidgetActivityJreSuite.java b/src/test/java/org/gwtproject/activity/widget/WidgetActivityJreSuite.java new file mode 100644 index 0000000..e72eb80 --- /dev/null +++ b/src/test/java/org/gwtproject/activity/widget/WidgetActivityJreSuite.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 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 org.gwtproject.activity.widget; + +import org.gwtproject.activity.widget.shared.WidgetActivityManagerTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * Tests of the activity package. + */ +@RunWith(Suite.class) +@Suite.SuiteClasses({ + WidgetActivityManagerTest.class +}) +public class WidgetActivityJreSuite { +} diff --git a/src/test/java/org/gwtproject/activity/widget/shared/WidgetActivityManagerTest.java b/src/test/java/org/gwtproject/activity/widget/shared/WidgetActivityManagerTest.java new file mode 100644 index 0000000..0f9807d --- /dev/null +++ b/src/test/java/org/gwtproject/activity/widget/shared/WidgetActivityManagerTest.java @@ -0,0 +1,593 @@ +/* + * 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 org.gwtproject.activity.widget.shared; + +import org.gwtproject.event.shared.Event; +import org.gwtproject.event.shared.EventBus; +import org.gwtproject.event.shared.UmbrellaException; +import org.gwtproject.event.shared.testing.CountingEventBus; +import org.gwtproject.place.shared.Place; +import org.gwtproject.place.shared.PlaceChangeEvent; +import org.gwtproject.place.shared.PlaceChangeRequestEvent; +import org.gwtproject.user.client.ui.AcceptsOneWidget; +import org.gwtproject.user.client.ui.IsWidget; +import org.gwtproject.user.client.ui.Widget; + +import junit.framework.TestCase; + +/** + * Eponymous unit test. + */ +public class WidgetActivityManagerTest extends TestCase { + private static class AsyncActivity extends SyncActivity { + AsyncActivity(MyView view) { + super(view); + } + + @Override + public void start(AcceptsOneWidget display, EventBus eventBus) { + this.display = display; + this.bus = eventBus; + } + + void finish() { + display.setWidget(view); + } + } + + private static class MyEvent extends Event { + private static Event.Type TYPE = new Event.Type(); + + @Override + public Event.Type getAssociatedType() { + throw new UnsupportedOperationException("Auto-generated method stub"); + } + + @Override + protected void dispatch(Handler handler) { + throw new UnsupportedOperationException("Auto-generated method stub"); + } + } + + private static class Handler { + }; + + private static class MyDisplay implements AcceptsOneWidget { + IsWidget view = null; + + @Override + public void setWidget(IsWidget view) { + this.view = view; + } + } + + private static class MyPlace extends Place { + } + + private static class MyView implements IsWidget { + @Override + public Widget asWidget() { + return null; + } + } + private static class SyncActivity implements WidgetActivity { + boolean canceled = false; + boolean stopped = false; + AcceptsOneWidget display; + String stopWarning; + MyView view; + EventBus bus; + + SyncActivity(MyView view) { + this.view = view; + } + + @Override + public String mayStop() { + return stopWarning; + } + + @Override + public void onCancel() { + canceled = true; + } + + @Override + public void onStop() { + stopped = true; + } + + @Override + public void start(AcceptsOneWidget display, EventBus eventBus) { + this.display = display; + this.bus = eventBus; + display.setWidget(view); + } + } + + private final MyPlace place1 = new MyPlace(); + private final MyPlace place2 = new MyPlace(); + + private SyncActivity activity1 = new SyncActivity(new MyView()); + + private SyncActivity activity2 = new SyncActivity(new MyView()); + + private final MyDisplay realDisplay = new MyDisplay(); + private final WidgetActivityMapper myMap = new WidgetActivityMapper() { + @Override + public WidgetActivity getActivity(Place place) { + if (place.equals(place1)) { + return activity1; + } + if (place.equals(place2)) { + return activity2; + } + + return null; + } + }; + + private CountingEventBus eventBus = new CountingEventBus(); + + private WidgetActivityManager manager = new WidgetActivityManager( + myMap, eventBus); + + public void testActiveEventBus() { + final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); + final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); + + WidgetActivityMapper map = new WidgetActivityMapper() { + @Override + public WidgetActivity getActivity(Place place) { + if (place.equals(place1)) { + return asyncActivity1; + } + if (place.equals(place2)) { + return asyncActivity2; + } + + return null; + } + }; + + manager = new WidgetActivityManager(map, eventBus); + manager.setDisplay(realDisplay); + + eventBus.fireEvent(new PlaceChangeEvent(place1)); + EventBus activeEventBus = manager.getActiveEventBus(); + + activeEventBus.addHandler(MyEvent.TYPE, new Handler()); + assertEquals(1, eventBus.getHandlerCount(MyEvent.TYPE)); + + eventBus.fireEvent(new PlaceChangeEvent(place2)); + assertEquals(0, eventBus.getHandlerCount(MyEvent.TYPE)); + } + + public void testAsyncDispatch() { + final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); + final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); + + WidgetActivityMapper map = new WidgetActivityMapper() { + @Override + public WidgetActivity getActivity(Place place) { + if (place.equals(place1)) { + return asyncActivity1; + } + if (place.equals(place2)) { + return asyncActivity2; + } + + return null; + } + }; + + manager = new WidgetActivityManager(map, eventBus); + manager.setDisplay(realDisplay); + + PlaceChangeRequestEvent event = new PlaceChangeRequestEvent(place1); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertNull(realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + assertNull(asyncActivity1.display); + + eventBus.fireEvent(new PlaceChangeEvent(place1)); + assertNull(realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + assertNotNull(asyncActivity1.display); + + asyncActivity1.finish(); + assertEquals(asyncActivity1.view, realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + + event = new PlaceChangeRequestEvent(place2); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertEquals(asyncActivity1.view, realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + assertFalse(asyncActivity2.stopped); + assertFalse(asyncActivity2.canceled); + assertNull(asyncActivity2.display); + + eventBus.fireEvent(new PlaceChangeEvent(place2)); + assertNull(realDisplay.view); + assertFalse(asyncActivity1.canceled); + assertTrue(asyncActivity1.stopped); + assertFalse(asyncActivity2.stopped); + assertFalse(asyncActivity2.canceled); + assertNotNull(asyncActivity2.display); + + asyncActivity2.finish(); + assertEquals(asyncActivity2.view, realDisplay.view); + } + + public void testCancel() { + final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); + final AsyncActivity ayncActivity2 = new AsyncActivity(new MyView()); + + WidgetActivityMapper map = new WidgetActivityMapper() { + @Override + public WidgetActivity getActivity(Place place) { + if (place.equals(place1)) { + return asyncActivity1; + } + if (place.equals(place2)) { + return ayncActivity2; + } + + return null; + } + }; + + manager = new WidgetActivityManager(map, eventBus); + manager.setDisplay(realDisplay); + + PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( + place1); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertNull(realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + assertNull(asyncActivity1.display); + + eventBus.fireEvent(new PlaceChangeEvent(place1)); + assertNull(realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + assertNotNull(asyncActivity1.display); + + event = new PlaceChangeRequestEvent(place2); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertNull(realDisplay.view); + assertFalse(asyncActivity1.stopped); + assertFalse(asyncActivity1.canceled); + + eventBus.fireEvent(new PlaceChangeEvent(place2)); + assertNull(realDisplay.view); + assertTrue(asyncActivity1.canceled); + assertFalse(asyncActivity1.stopped); + assertFalse(ayncActivity2.stopped); + assertFalse(ayncActivity2.canceled); + assertNotNull(ayncActivity2.display); + + ayncActivity2.finish(); + assertEquals(ayncActivity2.view, realDisplay.view); + + asyncActivity1.finish(); + assertEquals(ayncActivity2.view, realDisplay.view); + } + + public void testDropHandlersOnStop() { + manager.setDisplay(realDisplay); + + assertEquals(0, eventBus.getHandlerCount(MyEvent.TYPE)); + + activity1 = new SyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + bus.addHandler(MyEvent.TYPE, new Handler()); + } + + @Override + public void onStop() { + super.onStop(); + bus.addHandler(MyEvent.TYPE, new Handler()); + } + }; + + PlaceChangeEvent event = new PlaceChangeEvent(place1); + eventBus.fireEvent(event); + assertEquals(1, eventBus.getHandlerCount(MyEvent.TYPE)); + + event = new PlaceChangeEvent(place2); + eventBus.fireEvent(event); + assertEquals(0, eventBus.getHandlerCount(MyEvent.TYPE)); + + // Make sure we didn't nuke the ActivityManager's own handlers + assertEquals(1, eventBus.getHandlerCount(PlaceChangeEvent.TYPE)); + assertEquals(1, eventBus.getHandlerCount(PlaceChangeRequestEvent.TYPE)); + } + + public void testEventSetupAndTeardown() { + assertEquals(0, eventBus.getHandlerCount(PlaceChangeEvent.TYPE)); + assertEquals(0, eventBus.getHandlerCount(PlaceChangeRequestEvent.TYPE)); + + manager.setDisplay(realDisplay); + + assertEquals(1, eventBus.getHandlerCount(PlaceChangeEvent.TYPE)); + assertEquals(1, eventBus.getHandlerCount(PlaceChangeRequestEvent.TYPE)); + + manager.setDisplay(null); + + assertEquals(0, eventBus.getHandlerCount(PlaceChangeEvent.TYPE)); + assertEquals(0, eventBus.getHandlerCount(PlaceChangeRequestEvent.TYPE)); + } + + public void testExceptionsOnStartAndCancel() { + activity1 = new AsyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + bus.addHandler(MyEvent.TYPE, new Handler()); + } + @Override + public void onCancel() { + super.onCancel(); + bus.addHandler(MyEvent.TYPE, new Handler()); + throw new UnsupportedOperationException("Exception on cancel"); + } + }; + + activity2 = new SyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + throw new UnsupportedOperationException("Exception on start"); + } + }; + + manager.setDisplay(realDisplay); + + try { + PlaceChangeEvent event = new PlaceChangeEvent(place1); + eventBus.fireEvent(event); + assertEquals(1, eventBus.getHandlerCount(MyEvent.TYPE)); + + event = new PlaceChangeEvent(place2); + eventBus.fireEvent(event); + + fail("Expected exception"); + } catch (UmbrellaException e) { + // EventBus throws this one + assertEquals(1, e.getCauses().size()); + // And this is the one thrown by ActivityManager + UmbrellaException nested = (UmbrellaException) e.getCause(); + assertEquals(2, nested.getCauses().size()); + } + + assertTrue(activity1.canceled); + assertNotNull(activity2.display); + assertEquals(0, eventBus.getHandlerCount(MyEvent.TYPE)); + } + + public void testExceptionsOnStopAndStart() { + activity1 = new SyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + bus.addHandler(MyEvent.TYPE, new Handler()); + } + @Override + public void onStop() { + super.onStop(); + bus.addHandler(MyEvent.TYPE, new Handler()); + throw new UnsupportedOperationException("Exception on stop"); + } + }; + + activity2 = new SyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + throw new UnsupportedOperationException("Exception on start"); + } + }; + + manager.setDisplay(realDisplay); + + try { + PlaceChangeEvent event = new PlaceChangeEvent(place1); + eventBus.fireEvent(event); + assertEquals(1, eventBus.getHandlerCount(MyEvent.TYPE)); + + event = new PlaceChangeEvent(place2); + eventBus.fireEvent(event); + + fail("Expected exception"); + } catch (UmbrellaException e) { + // EventBus throws this one + assertEquals(1, e.getCauses().size()); + // And this is the one thrown by ActivityManager + UmbrellaException nested = (UmbrellaException) e.getCause(); + assertEquals(2, nested.getCauses().size()); + } + + assertTrue(activity1.stopped); + assertNotNull(activity2.display); + assertEquals(0, eventBus.getHandlerCount(MyEvent.TYPE)); + } + + /** + * @see http://code.google.com/p/google-web-toolkit/issues/detail?id=5375 + */ + public void testNullDisplayOnPlaceChange() { + manager.setDisplay(realDisplay); + + // Start an activity + manager.onPlaceChange(new PlaceChangeEvent(place1)); + + /* + * Now we're going to place2. During PlaceChangeEvent dispatch, + * someone kills the manager's display. + */ + manager.setDisplay(null); + + // Now the place change event reaches the manager + manager.onPlaceChange(new PlaceChangeEvent(place2)); + + assertNull(activity2.display); + assertTrue(activity1.stopped); + } + + public void testNullDisplayBeforeAsyncStart() { + final AsyncActivity asyncActivity1 = new AsyncActivity(new MyView()); + final AsyncActivity asyncActivity2 = new AsyncActivity(new MyView()); + + WidgetActivityMapper map = new WidgetActivityMapper() { + @Override + public WidgetActivity getActivity(Place place) { + if (place.equals(place1)) { + return asyncActivity1; + } + if (place.equals(place2)) { + return asyncActivity2; + } + + return null; + } + }; + + manager = new WidgetActivityManager(map, eventBus); + manager.setDisplay(realDisplay); + + // Start an activity + manager.onPlaceChange(new PlaceChangeEvent(place1)); + + // Kill the manager + manager.setDisplay(null); + + // The activity is ready to play + asyncActivity1.finish(); + + // Ta da, no NPE + } + + public void testRejected() { + manager.setDisplay(realDisplay); + + activity1.stopWarning = "Stop fool!"; + + PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( + place1); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertNull(realDisplay.view); + + eventBus.fireEvent(new PlaceChangeEvent(place1)); + assertEquals(activity1.view, realDisplay.view); + + event = new PlaceChangeRequestEvent(place2); + eventBus.fireEvent(event); + assertEquals(activity1.stopWarning, event.getWarning()); + assertEquals(activity1.view, realDisplay.view); + assertFalse(activity1.stopped); + assertFalse(activity1.canceled); + } + + public void testSyncDispatch() { + manager.setDisplay(realDisplay); + + PlaceChangeRequestEvent event = new PlaceChangeRequestEvent( + place1); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertNull(realDisplay.view); + assertFalse(activity1.stopped); + assertFalse(activity1.canceled); + + eventBus.fireEvent(new PlaceChangeEvent(place1)); + assertEquals(activity1.view, realDisplay.view); + assertFalse(activity1.stopped); + assertFalse(activity1.canceled); + + event = new PlaceChangeRequestEvent(place2); + eventBus.fireEvent(event); + assertNull(event.getWarning()); + assertEquals(activity1.view, realDisplay.view); + assertFalse(activity1.stopped); + assertFalse(activity1.canceled); + + eventBus.fireEvent(new PlaceChangeEvent(place2)); + assertEquals(activity2.view, realDisplay.view); + assertTrue(activity1.stopped); + assertFalse(activity1.canceled); + } + + /** + * Non-regression test: make sure an activity can call {@link Consumer#accept(IsWidget)} several times to switch views. + */ + public void testacceptSeveralTimesPerActivity() { + class TwoViewActivity extends SyncActivity { + MyView view2; + + public TwoViewActivity(MyView view1, MyView view2) { + super(view1); + this.view2 = view2; + } + + void secondView() { + display.setWidget(view2); + } + + void firstView() { + display.setWidget(view); + } + } + final TwoViewActivity activity = new TwoViewActivity(new MyView(), new MyView()); + + WidgetActivityMapper map = new WidgetActivityMapper() { + @Override + public WidgetActivity getActivity(Place place) { + return activity; + } + }; + + manager = new WidgetActivityManager(map, eventBus); + manager.setDisplay(realDisplay); + + // Start an activity + manager.onPlaceChange(new PlaceChangeEvent(place1)); + + assertEquals(activity.view, realDisplay.view); + + // Call accept on the display several times, just to make sure it's possible + activity.secondView(); + assertEquals(activity.view2, realDisplay.view); + + activity.firstView(); + assertEquals(activity.view, realDisplay.view); + + activity.secondView(); + assertEquals(activity.view2, realDisplay.view); + } +}