com.vaadin.data.provider.TreeDataProvider Maven / Gradle / Ivy
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.data.provider;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import com.vaadin.data.TreeData;
import com.vaadin.server.SerializableComparator;
import com.vaadin.server.SerializableFunction;
import com.vaadin.server.SerializablePredicate;
/**
* An in-memory data provider for listing components that display hierarchical
* data. Uses an instance of {@link TreeData} as its source of data.
*
* @author Vaadin Ltd
* @since 8.1
*
* @param
* data type
*/
public class TreeDataProvider
extends AbstractHierarchicalDataProvider>
implements InMemoryDataProvider {
private final TreeData treeData;
private SerializablePredicate filter = null;
private SerializableComparator sortOrder = null;
/**
* Constructs a new TreeDataProvider.
*
* This data provider should be refreshed after making changes to the
* underlying {@link TreeData} instance.
*
* @param treeData
* the backing {@link TreeData} for this provider, not
* {@code null}
*/
public TreeDataProvider(TreeData treeData) {
Objects.requireNonNull(treeData, "treeData cannot be null");
this.treeData = treeData;
}
/**
* Return the underlying hierarchical data of this provider.
*
* @return the underlying data of this provider
*/
public TreeData getTreeData() {
return treeData;
}
@Override
public boolean hasChildren(T item) {
if (!treeData.contains(item)) {
// The item might be dropped from the tree already
return false;
}
return !treeData.getChildren(item).isEmpty();
}
@Override
public int getChildCount(
HierarchicalQuery> query) {
// ensure speedy closing in case the stream is connected to IO channels
try (Stream stream = fetchChildren(query)) {
return (int) stream.count();
}
}
@Override
public Stream fetchChildren(
HierarchicalQuery> query) {
if (!treeData.contains(query.getParent())) {
throw new IllegalArgumentException("The queried item "
+ query.getParent()
+ " could not be found in the backing TreeData. "
+ "Did you forget to refresh this data provider after item removal?");
}
Stream childStream = getFilteredStream(
treeData.getChildren(query.getParent()).stream(),
query.getFilter());
Optional> comparing = Stream
.of(query.getInMemorySorting(), sortOrder)
.filter(c -> c != null)
.reduce((c1, c2) -> c1.thenComparing(c2));
if (comparing.isPresent()) {
childStream = childStream.sorted(comparing.get());
}
return childStream.skip(query.getOffset()).limit(query.getLimit());
}
@Override
public SerializablePredicate getFilter() {
return filter;
}
@Override
public void setFilter(SerializablePredicate filter) {
this.filter = filter;
refreshAll();
}
@Override
public SerializableComparator getSortComparator() {
return sortOrder;
}
@Override
public void setSortComparator(SerializableComparator comparator) {
sortOrder = comparator;
refreshAll();
}
@Override
public DataProvider withConvertedFilter(
SerializableFunction> filterConverter) {
Objects.requireNonNull(filterConverter,
"Filter converter can't be null");
return new DataProviderWrapper>(this) {
@Override
protected SerializablePredicate getFilter(Query query) {
return query.getFilter().map(filterConverter).orElse(null);
}
@Override
public int size(Query t) {
if (t instanceof HierarchicalQuery, ?>) {
return dataProvider.size(new HierarchicalQuery<>(
t.getOffset(), t.getLimit(), t.getSortOrders(),
t.getInMemorySorting(), getFilter(t),
((HierarchicalQuery) t).getParent()));
}
throw new IllegalArgumentException(
"Hierarchical data provider doesn't support non-hierarchical queries");
}
@Override
public Stream fetch(Query t) {
if (t instanceof HierarchicalQuery, ?>) {
return dataProvider.fetch(new HierarchicalQuery<>(
t.getOffset(), t.getLimit(), t.getSortOrders(),
t.getInMemorySorting(), getFilter(t),
((HierarchicalQuery) t).getParent()));
}
throw new IllegalArgumentException(
"Hierarchical data provider doesn't support non-hierarchical queries");
}
};
}
private Stream getFilteredStream(Stream stream,
Optional> queryFilter) {
final Optional> combinedFilter;
if (filter != null) {
combinedFilter = Optional
.of(queryFilter.map(filter::and).orElse(filter));
} else {
combinedFilter = queryFilter;
}
return combinedFilter.map(
f -> stream.filter(element -> flatten(element).anyMatch(f)))
.orElse(stream);
}
private Stream flatten(T element) {
return Stream.concat(Stream.of(element), getTreeData()
.getChildren(element).stream().flatMap(this::flatten));
}
}