For anyone who’d like to use a generic data provider with the Crud component
public class DataProvider<T extends Entity> extends AbstractBackEndDataProvider<T, CrudFilter> {
private final Class<T> typeParameterClass;
private final BaseServiceClass serviceClass;
private final List<T> DATABASE;
private Consumer<Long> sizeChangeListener;
public DataProvider(Class<T> typeParameterClass, BaseServiceClass serviceClass) {
this.typeParameterClass = typeParameterClass;
this.serviceClass = serviceClass;
DATABASE = serviceClass.findAll();
}
public void persist(T item) {
final Optional<T> existingItem = find(item.getId());
if (existingItem.isPresent()) {
int position = DATABASE.indexOf(existingItem.get());
DATABASE.remove(existingItem.get());
DATABASE.add(position, item);
} else {
DATABASE.add(item);
}
serviceClass.save(item);
}
public void delete(T item) {
DATABASE.removeIf(entity -> entity.getId().equals(item.getId()));
serviceClass.delete(item);
}
public void setSizeChangeListener(Consumer<Long> listener) {
sizeChangeListener = listener;
}
@Override
protected Stream<T> fetchFromBackEnd(Query<T, CrudFilter> query) {
int offset = query.getOffset();
int limit = query.getLimit();
Stream<T> stream = DATABASE.stream();
if (query.getFilter().isPresent())
stream = stream.filter(predicate(query.getFilter().get())).sorted(comparator(query.getFilter().get()));
return stream.skip(offset).limit(limit);
}
@Override
protected int sizeInBackEnd(Query<T, CrudFilter> query) {
long count = fetchFromBackEnd(query).count();
if (sizeChangeListener != null)
sizeChangeListener.accept(count);
return (int) count;
}
private Predicate<T> predicate(CrudFilter filter) {
return filter.getConstraints().entrySet().stream().map(constraint -> (Predicate<T>) obj -> {
try {
Object value = valueOf(constraint.getKey(), obj);
return value != null && value.toString().toLowerCase().contains(constraint.getValue().toLowerCase());
} catch (Exception e) {
e.printStackTrace();
return false;
}
}).reduce(Predicate::and).orElse(e -> true);
}
private Comparator<T> comparator(CrudFilter filter) {
return filter.getSortOrders().entrySet().stream().map(sortClause -> {
try {
Comparator<T> comparator = Comparator.comparing(obj -> (Comparable) valueOf(sortClause.getKey(), obj));
if (sortClause.getValue() == SortDirection.DESCENDING) {
comparator = comparator.reversed();
}
return comparator;
} catch (Exception ex) {
return (Comparator<T>) (o1, o2) -> 0;
}
}).reduce(Comparator::thenComparing).orElse((o1, o2) -> 0);
}
private Object valueOf(String fieldName, T obj) {
try {
Field field = typeParameterClass.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private Optional<T> find(ObjectId id) {
return DATABASE.stream().filter(entity -> entity.getId().equals(id)).findFirst();
}
}
DAO classes will extend BaseServiceClass
public abstract class BaseServiceClass<T> {
public abstract List<T> findAll();
public abstract void save(T item);
public abstract void delete(T item);
}
POJOs will implement the Entity interface.
public interface Entity {
ObjectId getId();
}
Usage is
DataProvider<Person> dataProvider = new DataProvider<>(Person.class, personService);