# QueryResultWindower.java

    package org.codefilarete.stalactite.spring.repository.query.execution.reduce;

    import java.util.List;
    import java.util.function.BiFunction;
    import java.util.function.Supplier;

    import org.codefilarete.stalactite.spring.repository.query.projection.PartTreeStalactiteProjection;
    import org.codefilarete.stalactite.spring.repository.query.domain.PartTreeStalactiteQuery;
    import org.codefilarete.stalactite.spring.repository.query.execution.StalactiteQueryMethodInvocationParameters;
    import org.codefilarete.stalactite.spring.repository.query.StalactiteRepositoryQuery;
    import org.springframework.data.domain.Pageable;

    /**
     * Technical class that applies windowing to a query result as Spring Data classes {@link org.springframework.data.domain.Slice}
     * and {@link org.springframework.data.domain.Page}.
     * The goal is to share the code that is necessary to {@link PartTreeStalactiteQuery} and {@link PartTreeStalactiteProjection}. However, its usage
     * is a bit complex (with callback, handler, etc.), thus it should be used with caution.
     *
     * @param <C>
     * @param <R>
     * @param <P>
     * @author Guillaume Mary
     * @see PageResultWindower
     * @see SliceResultWindower
     */
    public abstract class QueryResultWindower<C, R, P> {

    	private final StalactiteRepositoryQuery<C, ?> delegate;
    	protected final LimitHandler limitHandler;
    	private final BiFunction<StalactiteQueryMethodInvocationParameters, List<P>, R> queryResultSlicer;
    	private final Supplier<List<P>> resultSupplier;

    	public QueryResultWindower(StalactiteRepositoryQuery<C, ?> delegate,
    							   LimitHandler limitHandler,
    							   BiFunction<StalactiteQueryMethodInvocationParameters, List<P>, R> queryResultSlicer,
    							   Supplier<List<P>> resultSupplier) {
    		this.delegate = delegate;
    		this.limitHandler = limitHandler;
    		this.queryResultSlicer = queryResultSlicer;
    		this.resultSupplier = resultSupplier;
    	}

    	public R adaptExecution(Object[] parameters) {
    		StalactiteQueryMethodInvocationParameters invocationParameters = new StalactiteQueryMethodInvocationParameters(delegate.getQueryMethod(), parameters);
    		// windowing requires adapting the query to append a limit clause
    		adaptLimit(invocationParameters);
    		List<P> delegateResult = resultSupplier.get();
    		return queryResultSlicer.apply(invocationParameters, delegateResult);
    	}

    	protected void adaptLimit(StalactiteQueryMethodInvocationParameters invocationParameters) {
    		Pageable pageable = invocationParameters.getPageable();
    		// when the user asks for a page number (given Pageable is a Page instance or a Slice with page number) then we ask for the page number
    		limitHandler.limit(pageable.getPageSize(), (int) pageable.getOffset());
    	}
    }