/*
* Copyright 2014 cruxframework.org.
*
* 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.cruxframework.crux.core.client.dataprovider.pager;
import org.cruxframework.crux.core.client.collection.Array;
import org.cruxframework.crux.core.client.dataprovider.AbstractHasPagedDataProvider;
import org.cruxframework.crux.core.client.dataprovider.DataChangedEvent;
import org.cruxframework.crux.core.client.dataprovider.DataChangedHandler;
import org.cruxframework.crux.core.client.dataprovider.DataFilterEvent;
import org.cruxframework.crux.core.client.dataprovider.DataFilterHandler;
import org.cruxframework.crux.core.client.dataprovider.DataLoadStoppedEvent;
import org.cruxframework.crux.core.client.dataprovider.DataLoadStoppedHandler;
import org.cruxframework.crux.core.client.dataprovider.DataProvider;
import org.cruxframework.crux.core.client.dataprovider.DataProvider.SelectionMode;
import org.cruxframework.crux.core.client.dataprovider.DataProviderRecord;
import org.cruxframework.crux.core.client.dataprovider.DataSelectionEvent;
import org.cruxframework.crux.core.client.dataprovider.DataSelectionHandler;
import org.cruxframework.crux.core.client.dataprovider.DataSortedEvent;
import org.cruxframework.crux.core.client.dataprovider.DataSortedHandler;
import org.cruxframework.crux.core.client.dataprovider.FilterableProvider;
import org.cruxframework.crux.core.client.dataprovider.PageLoadedEvent;
import org.cruxframework.crux.core.client.dataprovider.PageLoadedHandler;
import org.cruxframework.crux.core.client.dataprovider.ResetEvent;
import org.cruxframework.crux.core.client.dataprovider.ResetHandler;
import org.cruxframework.crux.core.client.dataprovider.TransactionEndEvent;
import org.cruxframework.crux.core.client.dataprovider.TransactionEndHandler;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Panel;
/**
* Base implementation for Pageable widgets
* @author Thiago da Rosa de Bustamante
*/
public abstract class AbstractPageable<T, P extends IsWidget> extends AbstractHasPagedDataProvider<T> implements Pageable<T>
{
protected boolean allowRefreshAfterDataChange = true;
protected HandlerRegistration dataChangedHandler;
protected HandlerRegistration dataFilterHandler;
protected HandlerRegistration dataSortedHandler;
protected HandlerRegistration loadStoppedHandler;
protected HandlerRegistration pageLoadedHandler;
protected PageablePager<T> pager;
protected DataProvider.DataReader<T> reader = getDataReader();
protected HandlerRegistration transactionEndHandler;
private P pagePanel;
private HandlerRegistration resetHandler;
private HandlerRegistration dataSelectionHandler;
public void add(T object)
{
if (getDataProvider() != null)
{
getDataProvider().add(object);
}
}
public void commit()
{
if (getDataProvider() != null)
{
getDataProvider().commit();
}
}
public int indexOf(T object)
{
if (getDataProvider() != null)
{
return getDataProvider().indexOf(object);
}
return -1;
}
public boolean isDirty()
{
return getDataProvider() != null && getDataProvider().isDirty();
}
public void loadData()
{
if (getDataProvider() != null)
{
getDataProvider().load();
}
}
public void refresh()
{
refresh(true);
}
public void remove(int index)
{
if (getDataProvider() != null)
{
getDataProvider().remove(index);
}
}
public void reset()
{
reset(false);
}
public void reset(boolean reloadData)
{
if(getDataProvider() != null)
{
getDataProvider().reset();
}
if (reloadData)
{
loadData();
}
}
public void rollback()
{
if (getDataProvider() != null)
{
getDataProvider().rollback();
}
}
public void set(int index, T object)
{
if (getDataProvider() != null)
{
getDataProvider().set(index, object);
}
}
@Override
public void setHeight(String height)
{
if(pager != null && pager.supportsInfiniteScroll())
{
pager.asWidget().setHeight(height);
}
else
{
super.setHeight(height);
}
}
@Override
public void setPager(PageablePager<T> pager)
{
this.pager = pager;
if (pager != null)
{
if (getDataProvider() != null)
{
pager.setDataProvider(getDataProvider(), false);
}
pager.initializeContentPanel(getContentPanel());
if (pager.supportsInfiniteScroll())
{
ensurePageAndUpdatePagePanel(true);
}
else if (getPagePanel() != null)
{
pager.updatePagePanel(getPagePanel(), true);
}
}
}
@Override
protected void addDataProviderHandler()
{
pageLoadedHandler = getDataProvider().addPageLoadedHandler(new PageLoadedHandler()
{
@Override
public void onPageLoaded(final PageLoadedEvent event)
{
boolean renewPagePanel = pager == null || !pager.supportsInfiniteScroll();
if (renewPagePanel)
{
final IsWidget pagePanel = (pager != null)?doInitializePagePanel():null;
if (getPagePanel() == null) // no pager
{
IsWidget panel = doInitializePagePanel();
getContentPanel().add(panel);
};
render((pager == null), false, new RenderCallback()
{
@Override
public void onRendered()
{
if (pager != null)
{
pager.updatePagePanel(pagePanel, event.getCurrentPage() > event.getPreviousPage());
}
}
});
}
else
{
render(false, true, null);
}
}
});
loadStoppedHandler = getDataProvider().addLoadStoppedHandler(new DataLoadStoppedHandler()
{
@Override
public void onLoadStopped(DataLoadStoppedEvent event)
{
refresh();
}
});
transactionEndHandler = getDataProvider().addTransactionEndHandler(new TransactionEndHandler()
{
@Override
public void onTransactionEnd(TransactionEndEvent event)
{
onTransactionCompleted(event.isCommited());
}
});
dataChangedHandler = getDataProvider().addDataChangedHandler(new DataChangedHandler()
{
@Override
public void onDataChanged(DataChangedEvent event)
{
final int pageStartRecordOnTransactionEnd = getDataProvider().getCurrentPageStartRecord();
if(allowRefreshAfterDataChange)
{
refreshPage(pageStartRecordOnTransactionEnd);
}
}
});
dataSortedHandler = getDataProvider().addDataSortedHandler(new DataSortedHandler()
{
@Override
public void onSorted(DataSortedEvent event)
{
if (!event.isPageChanged())
{
final int pageStartRecordOnTransactionEnd = getDataProvider().getCurrentPageStartRecord();
refreshPage(pageStartRecordOnTransactionEnd);
}
}
});
if (getDataProvider() instanceof FilterableProvider<?>)
{
@SuppressWarnings("unchecked")
FilterableProvider<T> filterable = (FilterableProvider<T>) this.getDataProvider();
dataFilterHandler = filterable.addDataFilterHandler(new DataFilterHandler<T>()
{
@Override
public void onFiltered(DataFilterEvent<T> event)
{
refresh();
}
});
}
dataSelectionHandler = getDataProvider().addDataSelectionHandler(new DataSelectionHandler<T>()
{
@Override
public void onDataSelection(DataSelectionEvent<T> event)
{
if(getDataProvider().getSelectionMode().equals(SelectionMode.unselectable))
{
return;
}
Array<DataProviderRecord<T>> changedRecords = event.getChangedRecords();
if(changedRecords != null)
{
for(int i=0; i<changedRecords.size(); i++)
{
DataProviderRecord<T> dataProviderRecord = changedRecords.get(i);
onDataSelected(dataProviderRecord.getRecordObject(), dataProviderRecord.isSelected());
}
}
}
});
resetHandler = getDataProvider().addResetHandler(new ResetHandler()
{
@Override
public void onReset(ResetEvent event)
{
refresh();
}
});
}
protected abstract void onDataSelected(T recordObject, boolean selected);
protected abstract void clear();
protected abstract void clearRange(int startRecord);
protected void ensurePageAndUpdatePagePanel(boolean forward)
{
IsWidget pagePanel = this.pagePanel!=null?this.pagePanel:doInitializePagePanel();
if (pager != null)
{
pager.updatePagePanel(pagePanel, forward);
}
}
protected abstract Panel getContentPanel();
protected int getDataObjectIndex(int pageIndex)
{
int numPreviousPage = getDataProvider().getCurrentPage() - 1;
int dataObjectIndex = pageIndex + (numPreviousPage*getPageSize());
return dataObjectIndex;
}
protected int getDataObjectIndexForWidgetIndex(int widgetIndex)
{
int index;
if(this.pager != null && this.pager.supportsInfiniteScroll())
{
index = widgetIndex;
}
else
{
index = getDataObjectIndex(widgetIndex);
}
return index;
}
protected abstract DataProvider.DataReader<T> getDataReader();
/**
* Retrieve the panel that will contain all the page data.
* @return
*/
protected final P getPagePanel()
{
return pagePanel;
}
/**
* Creates the panel that will contain all the page data.
* @return
*/
protected abstract P initializePagePanel();
@Override
protected void onDataProviderSet()
{
if (getPagePanel() == null)
{
IsWidget panel = doInitializePagePanel();
getContentPanel().add(panel);
}
getDataProvider().first();
if (getDataProvider().isPageLoaded(1))
{
render(true, true, null);
}
}
protected void onTransactionCompleted(boolean commited)
{
final int pageStartRecordOnTransactionEnd = getDataProvider().getCurrentPageStartRecord();
Scheduler.get().scheduleDeferred(new ScheduledCommand()
{
@Override
public void execute()
{
refreshPage(pageStartRecordOnTransactionEnd);
}
});
}
protected void refresh(boolean goToFirstPage)
{
if (goToFirstPage && isDataLoaded())
{
getDataProvider().first();
}
render(goToFirstPage, true, null);
}
protected void refreshPage(int startRecord)
{
boolean refreshAll = pager == null || !pager.supportsInfiniteScroll();
if (refreshAll || startRecord < 0)
{
clear();
}
else
{
clearRange(startRecord);
}
render(false, false, null);
}
@Override
protected void removeDataProviderHandler()
{
if (transactionEndHandler != null)
{
transactionEndHandler.removeHandler();
transactionEndHandler = null;
}
if (loadStoppedHandler != null)
{
loadStoppedHandler.removeHandler();
loadStoppedHandler = null;
}
if (dataChangedHandler != null)
{
dataChangedHandler.removeHandler();
dataChangedHandler = null;
}
if (dataFilterHandler != null)
{
dataFilterHandler.removeHandler();
dataFilterHandler = null;
}
if (pageLoadedHandler != null)
{
pageLoadedHandler.removeHandler();
pageLoadedHandler = null;
}
if (dataSortedHandler != null)
{
dataSortedHandler.removeHandler();
dataSortedHandler = null;
}
if (resetHandler != null)
{
resetHandler.removeHandler();
resetHandler = null;
}
if (dataSelectionHandler != null)
{
dataSelectionHandler.removeHandler();
dataSelectionHandler = null;
}
}
protected void render(boolean refresh, boolean clearRange, RenderCallback callback)
{
if (refresh)
{
clear();
}
int rowCount = getRowsToBeRendered();
if (rowCount > 0)
{
getDataProvider().firstOnPage();
}
if (clearRange && !refresh && pager != null && pager.supportsInfiniteScroll())
{
int startRecord = getDataProvider().getCurrentPageStartRecord();
if (startRecord >= 0)
{
clearRange(startRecord);
}
else
{
clear();
}
}
for (int i=0; i<rowCount; i++)
{
getDataProvider().read(reader);
if (getDataProvider().hasNext())
{
getDataProvider().next();
}
else
{
break;
}
}
if (callback != null)
{
callback.onRendered();
}
}
protected void setForEdition(int index, T object)
{
if (getDataProvider() != null)
{
allowRefreshAfterDataChange = false;
getDataProvider().set(index, object);
allowRefreshAfterDataChange = true;
}
}
/**
* @return a Page Panel instance.
*/
private IsWidget doInitializePagePanel()
{
pagePanel = initializePagePanel();
return pagePanel;
}
private int getRowsToBeRendered()
{
if(isDataLoaded())
{
if(getDataProvider().getCurrentPage() == 0)
{
getDataProvider().nextPage();
}
return getDataProvider().getCurrentPageSize();
}
return 0;
}
protected static interface RenderCallback
{
void onRendered();
}
}