I was wondering if anyone had some ideas on how to solve a problem.
I wrote a DataProvider that fetches from a Cayenne-based backend. It extends AbstractBackEndDataProvider and implements BackEndDataProvider and ConfigurableFilterDataProvider.
It runs great but I’m bashing my head in trying to get it to have the ability to sort both in backend and in memory.
The backend is easy, but the in memory one I simply can’t seem to fix because the results are paginated.
My first attempt was to sort at the end of the implementation for:
public Stream<T> fetch(Query<T, Q> query)
Any request for a backended sorted content would pass the sortOrders to Cayenne and return an already sorted stream.
Instead, requests for in-memory sorted content would void the backend sort and perform a comparator based sort operation after getting the results back from Cayenne.
I seemed like it was working until I tried sorting in-memory with a list of more than 50 lines. The problem is pagination.
The backend data provider needs to get results paginated so fetch get’s called multiple times. Sorting there means you only sort the batch of 50 and not the full stream.
Backtracking from the fetch I found out that it is the Grid DataComunicator that is responsible for the call. So I wrote a very convoluted subclass of Grid that overrides constructors to set its own DataComunicator that allows me to intercept the paginated stream after it has been joined.
This is the overriden constructor:
public static class CayenneDataCommunicatorBuilder<T, U extends ArrayUpdater>
extends DataCommunicatorBuilder<T,U>
implements Serializable {
protected DataCommunicator<T> build(Element element,
CompositeDataGenerator<T> dataGenerator, U arrayUpdater,
SerializableSupplier<ValueProvider<T, String>> uniqueKeyProviderSupplier) {
return new DataCommunicator<>(
dataGenerator, arrayUpdater, data -> element
.callJsFunction("$connector.updateFlatData", data),
element.getNode()) {
@Override
protected Stream<T> fetchFromProvider(int offset, int limit) {
return super.fetchFromProvider(offset, limit);
// <---- SORT HERE
}
};
}
}
public CayenneGrid(Class<T> beanType) {
super(DEFAULT_PAGE_NUMBER, null,new CayenneDataCommunicatorBuilder<>());
configureBeanType(beanType, false);
}
Now this works albeit I honestly don’t think I should be catching the stream in the DataComunicator. The sorting should be in the DataProvider.
Then I tried it with a TreeGrid and I hit a wall because TreeGrid does not expose any constructor that allows me to pass a custom DataComunicator.
I just hope I’m doing something incredibly stupid and there’s a way simpler way of doing this that I haven’t seen.
Should I just disable pagination on a case-by-case basis so that when I fetch with in-memory sorting I get all the results in one go?
Any ideas?