Skip to content

Instantly share code, notes, and snippets.

@Legioth
Created June 23, 2020 19:47
Show Gist options
  • Save Legioth/482724d1260898e6e3d49675b530e2e7 to your computer and use it in GitHub Desktop.
Save Legioth/482724d1260898e6e3d49675b530e2e7 to your computer and use it in GitHub Desktop.
ListDataProvider that uses backend sorting definitions instead of comparators from the component
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import com.vaadin.flow.data.provider.InMemoryDataProviderHelpers;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.data.provider.QuerySortOrder;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.function.SerializablePredicate;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.internal.BeanUtil;
import com.vaadin.flow.internal.ReflectionCache;
public class BackendListDataProvider<T> extends ListDataProvider<T> {
private static final ReflectionCache<Object, ComparatorSupport> supportCache = new ReflectionCache<>(
ComparatorSupport::new);
private static class ComparatorSupport {
private final Class<?> type;
private final ConcurrentHashMap<String, Method> getters = new ConcurrentHashMap<>();
public ComparatorSupport(Class<?> type) {
this.type = type;
}
public Object getValue(Object bean, String propertyName) {
try {
return getGetter(propertyName).invoke(bean);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private Method getGetter(String propertyName) {
return getters.computeIfAbsent(propertyName, name -> {
try {
return BeanUtil.getPropertyDescriptor(type, name).getReadMethod();
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
});
}
}
private Map<String, Comparator<T>> customComparators = new HashMap<>();
public BackendListDataProvider(Collection<T> items) {
super(items);
}
@Override
public Stream<T> fetch(Query<T, SerializablePredicate<T>> query) {
List<QuerySortOrder> sortOrders = query.getSortOrders();
Comparator<T> inMemorySorting = sortOrders.stream().map(this::getComparator)
.reduce((c1, c2) -> c1.thenComparing(c2)).orElse(null);
return super.fetch(new Query<>(query.getOffset(), query.getLimit(), sortOrders, inMemorySorting,
query.getFilter().orElse(null)));
}
private Comparator<T> getComparator(QuerySortOrder order) {
String propertyName = order.getSorted();
Comparator<T> comparator = customComparators.get(propertyName);
if (comparator == null) {
comparator = createReflectComparator(propertyName);
}
if (order.getDirection() == SortDirection.DESCENDING) {
comparator = comparator.reversed();
}
return comparator;
}
private static <T> Comparator<T> createReflectComparator(String propertyName) {
return (o1, o2) -> {
Object v1 = supportCache.get(o1.getClass()).getValue(o1, propertyName);
Object v2 = supportCache.get(o2.getClass()).getValue(o2, propertyName);
return compareComparables(v1, v2);
};
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static int compareComparables(Object a, Object b) {
return ((Comparator) Comparator.nullsLast(Comparator.naturalOrder())).compare(a, b);
}
public void setCustomComparator(String propertyName, Comparator<T> comparator) {
customComparators.put(propertyName, comparator);
}
public <V extends Comparable<? super V>> void setCustomComparator(String propertyName,
ValueProvider<T, V> extractor) {
setCustomComparator(propertyName,
InMemoryDataProviderHelpers.propertyComparator(extractor, SortDirection.ASCENDING));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment