use of com.vaadin.flow.internal.Range in project flow by vaadin.
the class HierarchyMapperWithDataTest method fetchWithFilter.
@Test
public void fetchWithFilter() {
expand(testData.get(0));
// Expand second node
Node expandedNode = testData.get(2 + LEAF_COUNT);
expand(expandedNode);
SerializablePredicate<Node> filter = n -> n.getNumber() % 2 == 0;
// Root nodes plus children of expanded nodes 0 and 4 that match the
// filter
List<Node> expectedResult = IntStream.of(0, 1, 4, 6, 7, 10, 13, 26, 39, 52).mapToObj(testData::get).collect(Collectors.toList());
mapper.setFilter(filter);
// Fetch everything
Range range = Range.between(0, mapper.getTreeSize());
verifyFetchIsCorrect(expectedResult, range);
}
use of com.vaadin.flow.internal.Range in project flow by vaadin.
the class DataCommunicator method collectKeysToFlush.
private Activation collectKeysToFlush(final Range previousActive, final Range effectiveRequested) {
/*
* Collecting all items even though only some small sub range would
* actually be useful can be optimized away once we have some actual
* test coverage for the logic here.
*/
if (resendEntireRange) {
return activate(effectiveRequested);
} else {
List<String> newActiveKeyOrder = new ArrayList<>();
boolean sizeRecheckNeeded = false;
Range[] partitionWith = effectiveRequested.partitionWith(previousActive);
Activation activation = activate(partitionWith[0]);
newActiveKeyOrder.addAll(activation.getActiveKeys());
sizeRecheckNeeded |= activation.isSizeRecheckNeeded();
// Pick existing items from the current list
Range overlap = partitionWith[1].offsetBy(-activeStart);
if (overlap.getStart() < 0) {
// needs to be returned
return Activation.empty();
}
newActiveKeyOrder.addAll(activeKeyOrder.subList(overlap.getStart(), overlap.getEnd()));
activation = activate(partitionWith[2]);
newActiveKeyOrder.addAll(activation.getActiveKeys());
sizeRecheckNeeded |= activation.isSizeRecheckNeeded();
return new Activation(newActiveKeyOrder, sizeRecheckNeeded);
}
}
use of com.vaadin.flow.internal.Range in project flow by vaadin.
the class DataCommunicator method fetchFromProvider.
/**
* Fetches a list of items from the DataProvider.
* <p>
* <em>NOTE:</em> the {@code limit} parameter shows how many items the
* client wants to fetch, but the actual number of results may be greater,
* and vary from {@code 0 to pages * pageSize}.
*
* @param offset
* the starting index of the range
* @param limit
* the desired number of results
* @return the list of items in given range
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected Stream<T> fetchFromProvider(int offset, int limit) {
Stream<T> stream;
if (pagingEnabled) {
/*
* Items limit value may not be necessarily multiply of page size,
* and thus the pages count is rounded to closest smallest integer
* in order to overlap the requested range. Integer division is used
* here for simplicity and to avoid double-int-double conversion.
* Divisor minus one is placed on numerator part to ensure upwards
* rounding.
*/
final int pages = (limit - 1) / pageSize + 1;
if (limit > pageSize) {
/*
* Requested range is split to several pages, and queried from
* backend page by page
*/
final Stream.Builder<T> streamBuilder = Stream.builder();
final AtomicInteger fetchedPerPage = new AtomicInteger(0);
Consumer<T> addItemAndCheckConsumer = item -> {
streamBuilder.add(item);
fetchedPerPage.getAndIncrement();
};
// Keep fetching the pages until we get empty/partial page,
// or run out of pages to request
int page = 0;
do {
final int newOffset = offset + page * pageSize;
doFetchFromDataProvider(newOffset, pageSize).forEach(addItemAndCheckConsumer);
page++;
} while (page < pages && fetchedPerPage.getAndSet(0) == pageSize);
stream = streamBuilder.build();
} else {
stream = doFetchFromDataProvider(offset, pageSize);
}
limit = pages * pageSize;
} else {
stream = doFetchFromDataProvider(offset, limit);
}
if (stream.isParallel()) {
getLogger().debug("Data provider {} has returned parallel stream on 'fetch' call", getDataProvider().getClass());
stream = stream.collect(Collectors.toList()).stream();
assert !stream.isParallel();
}
SizeVerifier verifier = new SizeVerifier<>(limit);
return stream.peek(verifier);
}
use of com.vaadin.flow.internal.Range in project flow by vaadin.
the class HierarchicalCommunicationController method collectKeysToFlush.
private List<String> collectKeysToFlush(final Range previousActive, final Range effectiveRequested) {
List<String> newActiveKeyOrder;
/*
* Collecting all items even though only some small sub range would
* actually be useful can be optimized away once we have some actual
* test coverage for the logic here.
*/
if (resendEntireRange) {
newActiveKeyOrder = activate(effectiveRequested);
} else {
Range[] partitionWith = effectiveRequested.partitionWith(previousActive);
newActiveKeyOrder = new ArrayList<>();
newActiveKeyOrder.addAll(activate(partitionWith[0]));
// Pick existing items from the current list
Range overlap = partitionWith[1].offsetBy(-activeStart);
newActiveKeyOrder.addAll(activeKeyOrder.subList(overlap.getStart(), overlap.getEnd()));
newActiveKeyOrder.addAll(activate(partitionWith[2]));
}
return newActiveKeyOrder;
}
use of com.vaadin.flow.internal.Range in project flow by vaadin.
the class HierarchicalCommunicationController method flush.
public void flush() {
Set<String> oldActive = new HashSet<>(activeKeyOrder);
assumedSize = mapper.countChildItems(keyMapper.get(parentKey));
final Range previousActive = Range.withLength(activeStart, activeKeyOrder.size());
final Range effectiveRequested = requestedRange.restrictTo(Range.withLength(0, assumedSize));
resendEntireRange |= !(previousActive.intersects(effectiveRequested) || (previousActive.isEmpty() && effectiveRequested.isEmpty()));
// Phase 1: Find all items that the client should have
List<String> newActiveKeyOrder = collectKeysToFlush(previousActive, effectiveRequested);
activeKeyOrder = newActiveKeyOrder;
activeStart = effectiveRequested.getStart();
// Phase 2: Collect changes to send
HierarchicalUpdate update = startUpdate.apply(assumedSize);
boolean updated = collectChangesToSend(previousActive, effectiveRequested, update);
resendEntireRange = false;
assumeEmptyClient = false;
// Phase 3: passivate anything that isn't longer active
passivateInactiveKeys(oldActive, newActiveKeyOrder, update, updated);
// Phase 4: unregister passivated and updated items
unregisterPassivatedKeys();
}
Aggregations