package org.activityinfo.ui.client.component.table;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.view.client.ListDataProvider;
import org.activityinfo.core.client.InstanceQuery;
import org.activityinfo.core.client.QueryResult;
import org.activityinfo.core.shared.Projection;
import org.activityinfo.model.formTree.FieldPath;
import org.activityinfo.promise.Promise;
import org.activityinfo.ui.client.widget.CellTable;
import org.activityinfo.ui.client.widget.loading.LoadingState;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author yuriyz on 4/10/14.
*/
public class InstanceTableDataLoader {
private static final int PAGE_SIZE = 100;
public static interface DataLoadHandler extends EventHandler {
void onLoad(DataLoadEvent event);
}
public static class DataLoadEvent extends GwtEvent<DataLoadHandler> {
public static final Type<DataLoadHandler> TYPE = new Type<>();
private final int totalCount;
private final int loadedDataCount;
private boolean failed = false;
public DataLoadEvent(int totalCount, int loadedDataCount) {
this.totalCount = totalCount;
this.loadedDataCount = loadedDataCount;
}
public int getTotalCount() {
return totalCount;
}
public int getLoadedDataCount() {
return loadedDataCount;
}
public boolean isFailed() {
return failed;
}
public void setFailed(boolean failed) {
this.failed = failed;
}
@Override
public Type<DataLoadHandler> getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(DataLoadHandler handler) {
handler.onLoad(this);
}
}
private static final Logger LOGGER = Logger.getLogger(InstanceTableDataLoader.class.getName());
private final ListDataProvider<Projection> tableDataProvider = new ListDataProvider<>();
private final InstanceTable table;
private final Set<FieldPath> fields = Sets.newHashSet();
private int lastVerticalScrollPosition;
private int instanceTotalCount = -1;
public InstanceTableDataLoader(InstanceTable table) {
this.table = table;
tableDataProvider.addDataDisplay(table.getTable());
table.getTable().getEventBus().addHandler(CellTable.ScrollEvent.TYPE, new CellTable.ScrollHandler() {
@Override
public void onScroll(CellTable.ScrollEvent event) {
handleScroll(event);
}
});
}
private void handleScroll(CellTable.ScrollEvent event) {
final int oldScrollPos = lastVerticalScrollPosition;
lastVerticalScrollPosition = event.getVerticalScrollPosition();
// If scrolling up, ignore the event.
if (oldScrollPos >= lastVerticalScrollPosition) {
return;
}
int maxScrollTop = table.getTable().getOffsetHeight()
- event.getScrollAncestor().getOffsetHeight();
// if near the end then load data
if (lastVerticalScrollPosition >= maxScrollTop) {
// AI-524: as pointed in issue for now we will put "Load more" button instead of
// loading data on scrolling
// loadMore();
}
}
public void loadMore() {
final int offset = tableDataProvider.getList().size();
// load data only if offset is less then total count (and totalCount is initialized)
if (offset < instanceTotalCount && instanceTotalCount != -1) {
final int count = Math.min(PAGE_SIZE, instanceTotalCount - offset);
load(offset, count);
}
}
private Promise<QueryResult<Projection>> query(int offset, int count) {
table.getLoadingIndicator().onLoadingStateChanged(LoadingState.LOADING, null);
InstanceQuery query = new InstanceQuery(Lists.newArrayList(fields), table.buildQueryCriteria(), offset, count);
return table.getResourceLocator().queryProjection(query);
}
/**
* Loads data and append to table.
*
* @param offset offset
* @param count count
*/
private void load(int offset, int count) {
LOGGER.log(Level.FINE, "Loading instances... offset = " +
offset + ", count = " + count + ", fields = " + fields);
query(offset, count).then(new AsyncCallback<QueryResult<Projection>>() {
@Override
public void onFailure(Throwable caught) {
LOGGER.log(Level.SEVERE, "Failed to load instances. fields = " + fields, caught);
table.getLoadingIndicator().onLoadingStateChanged(LoadingState.FAILED, caught);
final DataLoadEvent dataLoadEvent = new DataLoadEvent(instanceTotalCount, tableDataProvider.getList().size());
dataLoadEvent.setFailed(true);
table.getTable().getEventBus().fireEvent(dataLoadEvent);
}
@Override
public void onSuccess(QueryResult<Projection> result) {
tableDataProvider.getList().addAll(result.getProjections());
instanceTotalCount = result.getTotalCount();
table.getTable().getEventBus().fireEvent(new DataLoadEvent(instanceTotalCount, tableDataProvider.getList().size()));
}
});
}
public void reload() {
tableDataProvider.getList().clear();
load(0, PAGE_SIZE);
}
public Set<FieldPath> getFields() {
return fields;
}
}