package com.wilutions.itol.db; import java.util.Collection; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.util.Duration; /** * Find suggestions for a given text after a given delay has passed. * * @param <T> * Item type */ public class DeferredSuggest<T> { private long deferMillis; private AtomicInteger suggestionCounter = new AtomicInteger(); private Suggest<T> suggest; public DeferredSuggest(Suggest<T> suggest, long deferMillis) { this.deferMillis = deferMillis; this.suggest = suggest; } /** * Find suggestions for given text. * Starts a timer that waits deferMillis milliseconds before it calls the suggest interface. * Right before the suggest interface is invoked, it is checked, whether another thread has * already invoked this function. In this case, the returned Future object is cancelled. * Otherwise the suggest interface is invoked and the Future object is completed with the results. * * @param text * Text * @param max * Maximum number of items to return. * @param ignoreHits * Return only items that do not exist in this collection. Can be null. * @return Future object that is completed, when the results are available. If the search is cancelled, the Future object is completed exceptionally. */ public CompletableFuture<Collection<T>> find(String text, int max, Collection<T> ignoreHits) { CompletableFuture<Collection<T>> ret = new CompletableFuture<>(); final int suggestionId = suggestionCounter.incrementAndGet(); Timeline timer = new Timeline(new KeyFrame(Duration.millis(deferMillis), (event) -> { if (suggestionId == suggestionCounter.get()) { CompletableFuture.supplyAsync(() -> { Collection<T> items = suggest.find(text, max, ignoreHits); return items; }).thenApply((items) -> { ret.complete(items); return null; }); } else { ret.completeExceptionally(new CancellationException()); } })); timer.setCycleCount(1); timer.play(); return ret; } }