package org.appfuse.webapp.client.application.base.activity;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javax.validation.ConstraintViolation;
import org.appfuse.webapp.client.application.Application;
import org.appfuse.webapp.client.application.base.place.EntityProxyPlace;
import org.appfuse.webapp.client.application.base.place.EntitySearchPlace;
import org.appfuse.webapp.client.application.base.view.ProxySearchView;
import org.appfuse.webapp.client.ui.home.HomePlace;
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.user.cellview.client.ColumnSortList;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.RangeChangeEvent;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;
/**
* 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 provide:
* <p/>
* <ul>
* <li>{@link #createView()}
* <li>{@link #createRequestContext()}
* <li>{@link #createCountRequest(RequestContext, BaseProxy)}
* <li>{@link #createSearchRequest(RequestContext, BaseProxy, Range, ColumnSortList)}
* </ul>
* <p/>
* Only the properties required by the view will be requested.
*
* @param <P> the type of {@link EntityProxy} listed
* @param <S> the type of {@link BaseProxy} acting as search criteria
*/
public abstract class AbstractProxySearchActivity<P extends EntityProxy, S> extends AbstractBaseActivity implements Activity, ProxySearchView.Delegate<P> {
protected final Logger logger = Logger.getLogger(getClass().getName());
private S searchCriteria;
protected final Class<S> searchCriteriaType;
protected final EntitySearchPlace currentPlace;
protected ProxySearchView<P, S> view;
private HandlerRegistration rangeChangeHandler;
protected abstract RequestContext createRequestContext();
protected abstract Request<Long> createCountRequest(RequestContext requestContext, S searchCriteria);
protected abstract Request<List<P>> createSearchRequest(RequestContext requestContext, S searchCriteria, Range range, ColumnSortList columnSortList);
public AbstractProxySearchActivity(final Application application, final ProxySearchView<P, S> view,
final Class<S> searchCriteriaType) {
super(application);
this.currentPlace = (EntitySearchPlace) application.getPlaceController().getWhere();
this.view = view;
this.searchCriteriaType = searchCriteriaType;
}
@Override
public void start(final AcceptsOneWidget panel, final EventBus eventBus) {
view.setDelegate(this);
panel.setWidget(view);
setDocumentTitleAndBodyAttributtes();
searchCriteria = (S) currentPlace.getSearchCriteria();
if(searchCriteria == null) {
searchCriteria = createSearchCriteria();
}
view.setSearchCriteria(searchCriteria);
if(currentPlace.getMaxResults() > 0) {
view.setPageSize(currentPlace.getMaxResults());
}
final HasData<P> hasData = view.asHasData();
rangeChangeHandler = hasData.addRangeChangeHandler(new RangeChangeEvent.Handler() {
@Override
public void onRangeChange(final RangeChangeEvent event) {
AbstractProxySearchActivity.this.onRangeChanged(hasData, hasData.getVisibleRange(), view.getColumnSortList());
}
});
// Select the current page range to load (by default or from place tokens)
Range range = hasData.getVisibleRange();
if(currentPlace.getFirstResult() > 0 ||
(currentPlace.getMaxResults() != range.getLength() && currentPlace.getMaxResults() > 0))
{
range = new Range(currentPlace.getFirstResult(), currentPlace.getMaxResults());
}
loadItems(searchCriteria, range);
}
protected S createSearchCriteria() {
if(!String.class.equals(searchCriteriaType)) {
return (S) proxyFactory.create((Class<BaseProxy>)searchCriteriaType);
}
return null;
}
protected void loadItems(final S searchCriteria) {
// Select the current page size to load
final Range currentRange = view.asHasData().getVisibleRange();
loadItems(searchCriteria, new Range(0, currentRange.getLength()));
}
/**
* Load items on start.
*/
protected void loadItems(final S searchCriteria, final Range range) {
if(searchCriteria instanceof BaseProxy) {
proxyFactory.setFrozen((BaseProxy) searchCriteria, true);
}
final RequestContext requestContext = createRequestContext();
createCountRequest(requestContext, searchCriteria).fire(new Receiver<Long>() {
@Override
public void onSuccess(final Long response) {
if (view == null) {
// This activity is dead
return;
}
view.asHasData().setRowCount(response.intValue(), true);
onRangeChanged(view.asHasData(), range, view.getColumnSortList());
}
});
}
/**
* Called by the table as it needs data.
*/
protected void onRangeChanged(final HasData<P> hasData, final Range range, final ColumnSortList columnSortList) {
final RequestContext requestContext = createRequestContext();
createSearchRequest(requestContext, searchCriteria, range, columnSortList)
.with(view.getPaths()).fire( new Receiver<List<P>>() {
@Override
public void onSuccess(final List<P> results) {
if (view == null) {
// This activity is dead
return;
}
hasData.setRowData(range.getStart(), results);
newHistoryToken(searchCriteria, range.getStart(), range.getLength());
}
});
}
protected void newHistoryToken(final S searchCriteria, final int firstResult, final int maxResults) {
final String historyToken = new EntitySearchPlace.Tokenizer(proxyFactory, requests)
.getFullHistoryToken(new EntitySearchPlace(currentPlace.getProxyClass(), firstResult, maxResults, searchCriteria));
History.newItem(historyToken, false);
}
@Override
public void addClicked() {
placeController.goTo(new EntityProxyPlace(currentPlace.getProxyClass()));
}
@Override
public void showDetails(final Class<? extends EntityProxy> proxyClass, final String entityId) {
placeController.goTo(new EntityProxyPlace(proxyClass, entityId, EntityProxyPlace.Operation.EDIT));
}
@Override
public void searchClicked() {
if(searchCriteria instanceof BaseProxy) {
proxyFactory.setFrozen((BaseProxy) searchCriteria, false);
}
searchCriteria = view.getSearchCriteria();
final Set<ConstraintViolation<S>> violations = validate(searchCriteria);
view.setConstraintViolations(violations);
if(violations == null || violations.isEmpty()) {
loadItems(searchCriteria);
}
}
/**
* Validates given searchCriteria.
*
* Override if you want to apply validation, example:
* <code><pre>
* protected Set<ConstraintViolation<S>> validate(S searchCriteria){
* return getValidator().validate(searchCriteria);
* }
* </pre></code>
* @param searchCriteria
* @return
*/
protected Set<ConstraintViolation<S>> validate(final S searchCriteria){
return null;//
}
@Override
public void deleteClicked(final Class<? extends EntityProxy> proxyClass, final String entityId) {
Window.alert("deleteClicked");
}
@Override
public void cancelClicked() {
placeController.goTo(new HomePlace());
}
@Override
public void onCancel() {
onStop();
}
@Override
public void onStop() {
view.setDelegate(null);
view = null;
rangeChangeHandler.removeHandler();
rangeChangeHandler = null;
}
}