/*
* 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;
import java.util.Comparator;
import java.util.List;
import org.cruxframework.crux.core.client.collection.Array;
import org.cruxframework.crux.core.client.collection.CollectionFactory;
/**
* @author Thiago da Rosa de Bustamante
*/
public class LazyDataProvider<T> extends AbstractPagedDataProvider<T> implements MeasurableLazyProvider<T>
{
protected int size;
protected LazyDataLoader<T> dataLoader;
public LazyDataProvider()
{
}
public LazyDataProvider(DataProvider.EditionDataHandler<T> handler)
{
super(handler);
this.data = CollectionFactory.createArray();
}
public LazyDataProvider(DataProvider.EditionDataHandler<T> handler, LazyDataLoader<T> dataLoader)
{
this(handler);
this.dataLoader = dataLoader;
}
@Override
public void setDataLoader(LazyDataLoader<T> dataLoader)
{
this.dataLoader = dataLoader;
}
@Override
public LazyDataLoader<T> getDataLoader()
{
return dataLoader;
}
@Override
public void stopLoading()
{
previousPage--;
currentPage--;
updateCurrentRecord();
super.stopLoading();
}
@Override
public void load()
{
if (!isLoaded() && dataLoader != null)
{
dataLoader.onMeasureData(new MeasureDataEvent<T>(this));
}
}
@Override
public void first()
{
if (currentPage != 1)
{
checkChanges();
}
super.first();
}
@Override
public void firstOnPage()
{
super.firstOnPage();
ensurePageLoaded(currentRecord);
}
@Override
public int size()
{
ensureLoaded();
return size;
}
@Override
public void last()
{
if (currentPage != getPageCount())
{
checkChanges();
}
super.last();
ensurePageLoaded(currentRecord);
}
@Override
public boolean nextPage()
{
checkChanges();
if (super.nextPage())
{
fetchCurrentPage(true);
return true;
}
return false;
}
@Override
public boolean previousPage()
{
checkChanges();
if (super.previousPage())
{
fetchCurrentPage(true);
return true;
}
return false;
}
@Override
public void reset()
{
if(data != null)
{
this.data = CollectionFactory.createArray();
}
currentRecord = -1;
currentPage = 0;
previousPage = -1;
loaded = false;
size = -1;
operations.reset();
fireResetEvent();
}
@Override
public void setSize(int recordCount)
{
this.size = recordCount;
if (this.size >= 0)
{
this.data = CollectionFactory.createArray(size);
setLoaded();
this.setCurrentPage(1);
}
}
@Override
public void setPageSize(int pageSize)
{
super.setPageSize(pageSize);
if (this.isLoaded() && dataLoader != null)
{
dataLoader.onMeasureData(new MeasureDataEvent<T>(this));
}
}
@Override
public void sort(Comparator<T> comparator)
{
ensurePageLoaded(currentRecord);
if (currentRecord > -1)
{
Array<DataProviderRecord<T>> pageData = CollectionFactory.createArray(pageSize);
int startPageRecord = getPageStartRecord();
int endPageRecord = getPageEndRecord();
int pageSize = endPageRecord - startPageRecord + 1;
for (int i = 0; i<pageSize; i++)
{
pageData.set(i, data.get(i+startPageRecord));
}
sortArray(pageData, comparator, true);
updateRecords(startPageRecord, endPageRecord, pageData);
fireSortedEvent(false);
}
}
@Override
public void setData(T[] data, int startRecord)
{
ensureLoaded();
if (data != null)
{
int dataSize = data.length;
Array<DataProviderRecord<T>> ret = CollectionFactory.createArray(dataSize);
for (int i = 0; i < dataSize; i++)
{
DataProviderRecord<T> record = new DataProviderRecord<T>(this);
record.setRecordObject(data[i]);
ret.set(i, record);
}
update(ret, startRecord, startRecord+dataSize-1);
}
}
@Override
public void setData(List<T> data, int startRecord)
{
ensureLoaded();
if (data != null)
{
int dataSize = data.size();
Array<DataProviderRecord<T>> ret = CollectionFactory.createArray(dataSize);
for (int i = 0; i < dataSize; i++)
{
DataProviderRecord<T> record = new DataProviderRecord<T>(this);
record.setRecordObject(data.get(i));
ret.set(i, record);
}
update(ret, startRecord, startRecord+dataSize-1);
}
}
@Override
public void setData(Array<T> data, int startRecord)
{
ensureLoaded();
if (data != null)
{
int dataSize = data.size();
Array<DataProviderRecord<T>> ret = CollectionFactory.createArray(dataSize);
for (int i = 0; i < dataSize; i++)
{
DataProviderRecord<T> record = new DataProviderRecord<T>(this);
record.setRecordObject(data.get(i));
ret.set(i, record);
}
update(ret, startRecord, startRecord+dataSize-1);
}
}
@Override
protected boolean setCurrentPage(int pageNumber, boolean fireEvents)
{
if (currentPage == pageNumber)
{
return true;
}
checkChanges();
if (super.setCurrentPage(pageNumber, fireEvents))
{
fetchCurrentPage(fireEvents);
return true;
}
return false;
}
@Override
protected void setFirstPosition(boolean fireEvents)
{
if (currentPage == 0 || hasPreviousPage() || !isPageLoaded(1))
{
setCurrentPage(1, fireEvents);
}
else
{
firstOnPage();
}
}
@Override
protected void update(Array<DataProviderRecord<T>> records)
{
size = records!= null?records.size():0;
data = CollectionFactory.createArray(size);
int startRecord = 0;
int endRecord = size -1;
int updateRecordCount = updateRecords(startRecord, endRecord, records);
if (updateRecordCount > 0)
{
setLoaded();
this.setCurrentPage(1);
}
}
protected void update(Array<DataProviderRecord<T>> records, int startRecord, int endRecord)
{
int updateRecordsCount = updateRecords(startRecord, endRecord, records);
if (updateRecordsCount > 0)
{
firePageLoadedEvent(startRecord, startRecord+updateRecordsCount-1);
}
else
{
firePageLoadedEvent(-1, -1);
}
}
protected void ensurePageLoaded(int recordNumber)
{
boolean loaded = isPageLoaded(getPageForRecord(recordNumber, false));
if (!loaded)
{
throw new DataProviderException("Error processing requested operation. DataProvider is not loaded yet.");
}
}
/**
*
*/
protected void fetchCurrentPage(boolean fireEvents)
{
int pageEndRecord = (currentPage * pageSize) - 1;
if (!isPageLoaded(currentPage))
{
if (dataLoader != null)
{
dataLoader.onFetchData(new FetchDataEvent<T>(this, getPageUnloadedStartRecord(), pageEndRecord));
}
}
else if (fireEvents)
{
firePageLoadedEvent(getPageStartRecord(), getPageEndRecord());
}
}
protected int getPageUnloadedStartRecord()
{
int pageStartRecord = getPageStartRecord();
int pageEndRecord = getPageEndRecord();
for (int i = pageStartRecord; i < pageEndRecord; i++)
{
if (data.get(i) == null)
{
return i;
}
}
return pageEndRecord;
}
public boolean isPageLoaded(int pageNumber)
{
if (!isLoaded())
{
return false;
}
int startPageRecord = getPageStartRecord(pageNumber);
int pageEndRecord = getPageEndRecord(pageNumber);
return (data.size() > 0 && data.get(startPageRecord) != null && data.get(pageEndRecord) != null);
}
/**
*
* @param startRecord
* @param endRecord
* @param records
* @return
*/
protected int updateRecords(int startRecord, int endRecord, Array<DataProviderRecord<T>> records)
{
if (records != null && endRecord < size)
{
for (int i = startRecord, j = 0; i <= endRecord && j < records.size(); i++, j++)
{
this.data.set(i, records.get(j));
}
return records.size();
}
return 0;
}
protected void checkChanges()
{
if (operations.isDirty())
{//TODO i18n
throw new DataProviderException("DataProvider has changes on page. You must save or discard them before perform this operation.");
}
}
@Override
protected void concludeEdition(boolean commited)
{
if (commited)
{
size = size + operations.getNewRecordsCount() - operations.getRemovedRecordsCount();
}
super.concludeEdition(commited);
}
}