/*
* Copyright 2015 Grow Bit
*
* 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.turbogwt.mvp;
import com.google.gwt.activity.shared.Activity;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import java.util.ArrayList;
/**
* Abstract class for every Presenter of the TurboGWT-MVP framework.
* It's also an Activity integrating itself to the GWT A&P framework.
* <p>
* As an Activity, it's always willing to stop.
* As a Presenter it provides access to its View as well as the app's EventBus and PlaceController.
* Additionally it can hold HandlerRegistration instances (via {@link #addHandlerRegistration(HandlerRegistration)}) to
* cancel them when the Activity is stopped/cancelled.
*
* @param <T> The View which this Presenter is attached
*
* @author Danilo Reinert
*/
public abstract class AbstractPresenter<T extends View> implements Activity, Presenter {
private final T view;
private final PlaceController placeController;
private final ArrayList<HandlerRegistration> handlers;
private AcceptsOneWidget panel;
private EventBus eventBus;
protected AbstractPresenter(T view, PlaceController placeController) {
this.view = view;
this.placeController = placeController;
this.handlers = new ArrayList<HandlerRegistration>();
}
/**
* Called when the Activity (Presenter) is about to be displayed.
* <p>
* Here, the Presenter must set up its View for the user.
* <p>
* At this point, the event bus is set and is accessible from {@link #getEventBus()}.
* <p>
* When the View is ready, Presenter must display it by calling {@link #display()}.
* <p>
* Notice that the Presenter may display the View only after receiving a response from an async request.
* <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 HandlerRegistration} instances returned by {@link EventBus#addHandler}.
*/
public abstract void onStart();
@Override
@SuppressWarnings("unchecked")
public final void start(AcceptsOneWidget panel, com.google.gwt.event.shared.EventBus eventBus) {
this.eventBus = eventBus;
this.panel = panel;
view.setPresenter(this);
onStart();
}
@Override
public void goTo(Place place) {
placeController.goTo(place);
}
/**
* Called when the user is trying to navigate away from this activity.
* <p>
* If it returns a non-null string, then the user is prompted with this message (via a native alert) to confirm or
* cancel the navigation operation.
* <p>
* It's often used in form-like views, when the user must save the changes before moving out.
*
* @return A message to display to the user, e.g. to warn of unsaved work, or null to say nothing
*/
@Override
public String mayStop() {
return null;
}
/**
* Called when {@link #onStart} has not yet replied displayed the View, but the user has lost interest.
* <p>
* It's a good practice to call super.onCancel() when overriding this method to avoid leaks in the app.
*/
@Override
public void onCancel() {
clear();
}
/**
* Called when the View has been undisplayed.
* All event handlers registered in this Activity (Presenter) will have been removed before this method is called.
* <p>
* It's a good practice to call super.onStop() when overriding this method to avoid leaks in the app.
*/
@Override
public void onStop() {
clear();
}
/**
* Hold a {@link HandlerRegistration} in this Activity, so when the Activity is
* stopped/cancelled the registrations are cancelled automatically.
*
* @param handlerRegistration the handler registration to be cancelled when the Activity is gone.
*/
protected void addHandlerRegistration(HandlerRegistration handlerRegistration) {
handlers.add(handlerRegistration);
}
/**
* Presents the view to the user and completes the start process of the Activity.
* Must be called in the {@link #onStart()) method.
*/
protected void display() {
if (panel == null) {
throw new IllegalStateException("The view is not starting yet." +
" This method must be called only in the #onStart method.");
}
panel.setWidget(view);
}
/**
* Retrieve the event bus instance associated to a specific place request event.
* <p>
* It is available only when the Activity is starting, i.e.,
* after the {@link #onStart()) method is triggered.
*
* @return the event bus
*/
protected EventBus getEventBus() {
return eventBus;
}
protected PlaceController getPlaceController() {
return placeController;
}
protected T getView() {
return view;
}
/**
* Cancels all HandlerRegistrations added via {@link #addHandlerRegistration(HandlerRegistration)},
* detaches this Presenter from the View and invalidates {@link #getEventBus()} and {@link #display()} methods.
*/
@SuppressWarnings("unchecked")
private void clear() {
for (HandlerRegistration handler : handlers) {
handler.removeHandler();
}
handlers.clear();
view.setPresenter(null);
panel = null;
eventBus = null;
}
}