/* * Copyright 2000-2016 Vaadin Ltd. * * 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 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.HierarchyData; import com.vaadin.data.ValueProvider; import com.vaadin.server.SerializableComparator; import com.vaadin.server.SerializableFunction; import com.vaadin.server.SerializablePredicate; import com.vaadin.shared.data.sort.SortDirection; /** * A {@link DataProvider} for in-memory hierarchical data. * * @see HierarchyData * * @author Vaadin Ltd * @since 8.1 * * @param <T> * data type */ public class InMemoryHierarchicalDataProvider<T> extends AbstractHierarchicalDataProvider<T, SerializablePredicate<T>> implements ConfigurableFilterDataProvider<T, SerializablePredicate<T>, SerializablePredicate<T>> { private final HierarchyData<T> hierarchyData; private SerializablePredicate<T> filter = null; private SerializableComparator<T> sortOrder = null; /** * Constructs a new InMemoryHierarchicalDataProvider. * <p> * All changes made to the given HierarchyData object will also be visible * through this data provider. * * @param hierarchyData * the backing HierarchyData for this provider */ public InMemoryHierarchicalDataProvider(HierarchyData<T> hierarchyData) { this.hierarchyData = hierarchyData; } /** * Return the underlying hierarchical data of this provider. * * @return the underlying data of this provider */ public HierarchyData<T> getData() { return hierarchyData; } @Override public boolean isInMemory() { return true; } @Override public boolean hasChildren(T item) { if (!hierarchyData.contains(item)) { throw new IllegalArgumentException("Item " + item + " could not be found in the backing HierarchyData. " + "Did you forget to refresh this data provider after item removal?"); } return !hierarchyData.getChildren(item).isEmpty(); } @Override public int getChildCount( HierarchicalQuery<T, SerializablePredicate<T>> query) { return (int) fetchChildren(query).count(); } @Override public Stream<T> fetchChildren( HierarchicalQuery<T, SerializablePredicate<T>> query) { if (!hierarchyData.contains(query.getParent())) { throw new IllegalArgumentException("The queried item " + query.getParent() + " could not be found in the backing HierarchyData. " + "Did you forget to refresh this data provider after item removal?"); } Stream<T> childStream = getFilteredStream( hierarchyData.getChildren(query.getParent()).stream(), query.getFilter()); Optional<Comparator<T>> 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 void setFilter(SerializablePredicate<T> filter) { this.filter = filter; refreshAll(); } /** * Adds a filter to be applied to all queries. The filter will be used in * addition to any filter that has been set or added previously. * * @see #addFilter(ValueProvider, SerializablePredicate) * @see #addFilterByValue(ValueProvider, Object) * @see #setFilter(SerializablePredicate) * * @param filter * the filter to add, not <code>null</code> */ public void addFilter(SerializablePredicate<T> filter) { Objects.requireNonNull(filter, "Filter cannot be null"); if (this.filter == null) { setFilter(filter); } else { SerializablePredicate<T> oldFilter = this.filter; setFilter(item -> oldFilter.test(item) && filter.test(item)); } } /** * Sets the comparator to use as the default sorting for this data provider. * This overrides the sorting set by any other method that manipulates the * default sorting of this data provider. * <p> * The default sorting is used if the query defines no sorting. The default * sorting is also used to determine the ordering of items that are * considered equal by the sorting defined in the query. * * @see #setSortOrder(ValueProvider, SortDirection) * @see #addSortComparator(SerializableComparator) * * @param comparator * a comparator to use, or <code>null</code> to clear any * previously set sort order */ public void setSortComparator(SerializableComparator<T> comparator) { sortOrder = comparator; refreshAll(); } /** * Adds a comparator to the default sorting for this data provider. If no * default sorting has been defined, then the provided comparator will be * used as the default sorting. If a default sorting has been defined, then * the provided comparator will be used to determine the ordering of items * that are considered equal by the previously defined default sorting. * <p> * The default sorting is used if the query defines no sorting. The default * sorting is also used to determine the ordering of items that are * considered equal by the sorting defined in the query. * * @see #setSortComparator(SerializableComparator) * @see #addSortOrder(ValueProvider, SortDirection) * * @param comparator * a comparator to add, not <code>null</code> */ public void addSortComparator(SerializableComparator<T> comparator) { Objects.requireNonNull(comparator, "Sort order to add cannot be null"); SerializableComparator<T> originalComparator = sortOrder; if (originalComparator == null) { setSortComparator(comparator); } else { setSortComparator((a, b) -> { int result = originalComparator.compare(a, b); if (result == 0) { result = comparator.compare(a, b); } return result; }); } } @Override public <C> DataProvider<T, C> withConvertedFilter( SerializableFunction<C, SerializablePredicate<T>> filterConverter) { Objects.requireNonNull(filterConverter, "Filter converter can't be null"); return new DataProviderWrapper<T, C, SerializablePredicate<T>>(this) { @Override protected SerializablePredicate<T> getFilter(Query<T, C> query) { return query.getFilter().map(filterConverter).orElse(null); } @Override public int size(Query<T, C> t) { if (t instanceof HierarchicalQuery<?, ?>) { return dataProvider.size(new HierarchicalQuery<>( t.getOffset(), t.getLimit(), t.getSortOrders(), t.getInMemorySorting(), getFilter(t), ((HierarchicalQuery<T, C>) t).getParent())); } throw new IllegalArgumentException( "Hierarchical data provider doesn't support non-hierarchical queries"); } @Override public Stream<T> fetch(Query<T, C> t) { if (t instanceof HierarchicalQuery<?, ?>) { return dataProvider.fetch(new HierarchicalQuery<>( t.getOffset(), t.getLimit(), t.getSortOrders(), t.getInMemorySorting(), getFilter(t), ((HierarchicalQuery<T, C>) t).getParent())); } throw new IllegalArgumentException( "Hierarchical data provider doesn't support non-hierarchical queries"); } }; } private Stream<T> getFilteredStream(Stream<T> stream, Optional<SerializablePredicate<T>> queryFilter) { if (filter != null) { stream = stream.filter(filter); } return queryFilter.map(stream::filter).orElse(stream); } }