All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jooq.lambda.WindowImpl Maven / Gradle / Ivy

Go to download

jOOλ is part of the jOOQ series (along with jOOQ, jOOX, jOOR, jOOU) providing some useful extensions to Java 8 lambdas.

There is a newer version: 0.9.15
Show newest version
/**
 * Copyright (c), Data Geekery GmbH, [email protected]
 *
 * 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 org.jooq.lambda;

import java.util.Collection;
import java.util.Collections;

import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.comparing;
import static java.util.Collections.binarySearch;
import static org.jooq.lambda.Seq.seq;
import static org.jooq.lambda.tuple.Tuple.tuple;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import org.jooq.lambda.tuple.Tuple2;

/**
 * @author Lukas Eder
 */
class WindowImpl implements Window {

    final Tuple2       value;
    final int                   index;
    final Partition          partition;
    final Comparator order;
    final long                  lower;
    final long                  upper;

    @SuppressWarnings({ "unchecked" })
    WindowImpl(
        Tuple2 value,
        Partition partition,
        WindowSpecification specification
    ) {
        this.value = value;
        this.partition = partition;
        this.order = specification.order().orElse((Comparator) naturalOrder());
        this.lower = specification.lower();
        this.upper = specification.upper();

        int i = specification.order().isPresent()
              ? binarySearch(partition.list, value, comparing((Tuple2 t) -> t.v1, specification.order().get()).thenComparing(t -> t.v2))
              : binarySearch(partition.list, value, comparing(t -> t.v2));
        this.index = (i >= 0 ? i : -i - 1);
    }

    // Accessor methods
    // -------------------------------------------------------------------------

    @Override
    public T value() {
        return value.v1;
    }

    @Override
    public Seq window() {
        return Seq.seq(partition.list.subList(lower(), upper() + 1)).map(t -> t.v1);
    }

    // Utilities
    // -------------------------------------------------------------------------

    private int lower() {
        // TODO: What about under/overflows?
        return lower == Long.MIN_VALUE ? 0 : (int) Math.max(0L, index + lower);
    }

    private boolean lowerInPartition() {
        // TODO: What about under/overflows?
        return lower == Long.MIN_VALUE || (index + lower >= 0L && index + lower < partition.list.size());
    }

    private int upper() {
        // TODO: What about under/overflows?
        return upper == Long.MAX_VALUE ? partition.list.size() - 1 : (int) Math.min(partition.list.size() - 1, (index + upper));
    }

    private boolean upperInPartition() {
        // TODO: What about under/overflows?
        return upper == Long.MAX_VALUE || (index + upper >= 0L && index + upper < partition.list.size());
    }

    private boolean completePartition() {
        return count() == partition.list.size();
    }

    // Ranking functions
    // -------------------------------------------------------------------------

    @Override
    public long rowNumber() {
        return (long) index;
    }

    @Override
    public long rank() {
        return seq(partition.list).map(t -> t.v1).collect(Agg.rank(value.v1, order)).get();
    }

    @Override
    public long denseRank() {
        return seq(partition.list).map(t -> t.v1).collect(Agg.denseRank(value.v1, order)).get();
    }

    @Override
    public double percentRank() {
        return ((double) rank()) / ((double) (partition.list.size() - 1));
    }

    @Override
    public long ntile(long bucket) {
        return (bucket * rowNumber() / partition.list.size());
    }

    @Override
    public Optional lead() {
        return lead(1);
    }

    @Override
    public Optional lead(long lead) {
        return lead0(lead);
    }

    @Override
    public Optional lag() {
        return lag(1);
    }

    @Override
    public Optional lag(long lag) {
        return lead0(-lag);
    }

    private Optional lead0(long lead) {
        if (lead == 0)
            return Optional.of(value.v1);
        else if (index + lead >= 0 && index + lead < partition.list.size())
            return Optional.of(partition.list.get(index + (int) lead).v1);
        else
            return Optional.empty();
    }

    @Override
    public Optional firstValue() {
        return firstValue(t -> t);
    }

    @Override
    public  Optional firstValue(Function function) {
        return lowerInPartition()
             ? Optional.of(function.apply(partition.list.get(lower()).v1))
             : upperInPartition()
             ? Optional.of(function.apply(partition.list.get(0).v1))
             : Optional.empty();
    }

    @Override
    public Optional lastValue() {
        return lastValue(t -> t);
    }

    @Override
    public  Optional lastValue(Function function) {
        return upperInPartition()
             ? Optional.of(function.apply(partition.list.get(upper()).v1))
             : lowerInPartition()
             ? Optional.of(function.apply(partition.list.get(partition.list.size() - 1).v1))
             : Optional.empty();
    }

    @Override
    public Optional nthValue(long n) {
        return nthValue(n, t -> t);
    }

    @Override
    public  Optional nthValue(long n, Function function) {
        return lower() + n <= upper()
             ? Optional.of(function.apply(partition.list.get(lower() + (int) n).v1))
             : Optional.empty();
    }

    // Aggregate functions
    // -------------------------------------------------------------------------

    @Override
    public long count() {
        return 1 + upper() - lower();
    }

    @Override
    public long count(Predicate predicate) {
        return partition.cacheIf(completePartition(), tuple("count", predicate), () -> window().count(predicate));
    }

    @Override
    public long countDistinct() {
        return partition.cacheIf(completePartition(), "countDistinct", () -> window().countDistinct());
    }

    @Override
    public long countDistinct(Predicate predicate) {
        return partition.cacheIf(completePartition(), tuple("countDistinct", predicate), () -> window().countDistinct(predicate));
    }

    @Override
    public  long countDistinctBy(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("countDistinctBy", function), () -> window().countDistinctBy(function));
    }

    @Override
    public  long countDistinctBy(Function function, Predicate predicate) {
        return partition.cacheIf(completePartition(), tuple("countDistinctBy", function, predicate), () -> window().countDistinctBy(function, predicate));
    }

    @Override
    public Optional sum() {
        return partition.cacheIf(completePartition(), "sum", () -> window().sum());
    }

    @Override
    public  Optional sum(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("sum", function), () -> window().sum(function));
    }

    @Override
    public int sumInt(ToIntFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("sumInt", function), () -> window().sumInt(function));
    }

    @Override
    public long sumLong(ToLongFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("sumLong", function), () -> window().sumLong(function));
    }

    @Override
    public double sumDouble(ToDoubleFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("sumDouble", function), () -> window().sumDouble(function));
    }

    @Override
    public Optional avg() {
        return partition.cacheIf(completePartition(), "avg", () -> window().avg());
    }

    @Override
    public  Optional avg(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("avg", function), () -> window().avg(function));
    }

    @Override
    public double avgInt(ToIntFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("avgInt", function), () -> window().avgInt(function));
    }

    @Override
    public double avgLong(ToLongFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("avgLong", function), () -> window().avgLong(function));
    }

    @Override
    public double avgDouble(ToDoubleFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("avgDouble", function), () -> window().avgDouble(function));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Optional min() {
        return partition.cacheIf(completePartition(), "min", () -> window().min((Comparator) naturalOrder()));
    }

    @Override
    public Optional min(Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("min", comparator), () -> window().min(comparator));
    }

    @Override
    public > Optional min(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("min", function), () -> window().min(function));
    }

    @Override
    public  Optional min(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("min", function, comparator), () -> window().min(function, comparator));
    }

    @Override
    public > Optional minBy(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("minBy", function), () -> window().minBy(function));
    }

    @Override
    public  Optional minBy(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("minBy", function, comparator), () -> window().minBy(function, comparator));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Seq minAll() {
        return partition.cacheIf(completePartition(), "minAll", () -> window().minAll((Comparator) naturalOrder()));
    }

    @Override
    public Seq minAll(Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("minAll", comparator), () -> window().minAll(comparator));
    }

    @Override
    public > Seq minAll(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("minAll", function), () -> window().minAll(function));
    }

    @Override
    public  Seq minAll(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("minAll", function, comparator), () -> window().minAll(function, comparator));
    }

    @Override
    public > Seq minAllBy(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("minAllBy", function), () -> window().minAllBy(function));
    }

    @Override
    public  Seq minAllBy(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("minAllBy", function, comparator), () -> window().minAllBy(function, comparator));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Optional max() {
        return partition.cacheIf(completePartition(), "max", () -> window().max((Comparator) naturalOrder()));
    }

    @Override
    public Optional max(Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("max", comparator), () -> window().max(comparator));
    }

    @Override
    public > Optional max(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("max", function), () -> window().max(function));
    }

    @Override
    public  Optional max(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("max", function, comparator), () -> window().max(function, comparator));
    }

    @Override
    public > Optional maxBy(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("maxBy", function), () -> window().maxBy(function));
    }

    @Override
    public  Optional maxBy(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("maxBy", function, comparator), () -> window().maxBy(function, comparator));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Seq maxAll() {
        return partition.cacheIf(completePartition(), "maxAll", () -> window().maxAll((Comparator) naturalOrder()));
    }

    @Override
    public Seq maxAll(Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("maxAll", comparator), () -> window().maxAll(comparator));
    }

    @Override
    public > Seq maxAll(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("maxAll", function), () -> window().maxAll(function));
    }

    @Override
    public  Seq maxAll(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("maxAll", function, comparator), () -> window().maxAll(function, comparator));
    }

    @Override
    public > Seq maxAllBy(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("maxAllBy", function), () -> window().maxAllBy(function));
    }

    @Override
    public  Seq maxAllBy(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("maxAllBy", function, comparator), () -> window().maxAllBy(function, comparator));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Optional median() {
        return partition.cacheIf(completePartition(), "median", () -> window().median((Comparator) naturalOrder()));
    }

    @Override
    public Optional median(Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("median", comparator), () -> window().median(comparator));
    }

    @Override
    public > Optional medianBy(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("medianBy", function), () -> window().medianBy(function));
    }

    @Override
    public  Optional medianBy(Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("medianBy", function, comparator), () -> window().medianBy(function, comparator));
    }

    @Override
    @SuppressWarnings("unchecked")
    public Optional percentile(double percentile) {
        return partition.cacheIf(completePartition(), () -> tuple("percentile", percentile), () -> window().percentile(percentile, (Comparator) naturalOrder()));
    }

    @Override
    public Optional percentile(double percentile, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("percentile", percentile, comparator), () -> window().percentile(percentile, comparator));
    }

    @Override
    public > Optional percentileBy(double percentile, Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("percentileBy", percentile, function), () -> window().percentileBy(percentile, function));
    }

    @Override
    public  Optional percentileBy(double percentile, Function function, Comparator comparator) {
        return partition.cacheIf(completePartition(), () -> tuple("percentileBy", percentile, function, comparator), () -> window().percentileBy(percentile, function, comparator));
    }

    @Override
    public Optional mode() {
        return partition.cacheIf(completePartition(), "mode", () -> window().mode());
    }

    @Override
    public  Optional modeBy(Function function) {
        return partition.cacheIf(completePartition(), tuple("modeBy", function), () -> window().modeBy(function));
    }

    @Override
    public Seq modeAll() {
        return partition.cacheIf(completePartition(), "modeAll", () -> window().modeAll());
    }

    @Override
    public  Seq modeAllBy(Function function) {
        return partition.cacheIf(completePartition(), tuple("modeAllBy", function), () -> window().modeAllBy(function));
    }

    @Override
    public boolean allMatch(Predicate predicate) {
        return partition.cacheIf(completePartition(), () -> tuple("allMatch", predicate), () -> window().allMatch(predicate));
    }

    @Override
    public boolean anyMatch(Predicate predicate) {
        return partition.cacheIf(completePartition(), () -> tuple("anyMatch", predicate), () -> window().anyMatch(predicate));
    }

    @Override
    public boolean noneMatch(Predicate predicate) {
        return partition.cacheIf(completePartition(), () -> tuple("noneMatch", predicate), () -> window().noneMatch(predicate));
    }

    @Override
    public Optional bitAnd() {
        return partition.cacheIf(completePartition(), () -> tuple("bitAnd"), () -> window().bitAnd());
    }

    @Override
    public  Optional bitAnd(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("bitAnd", function), () -> window().bitAnd(function));
    }

    @Override
    public int bitAndInt(ToIntFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("bitAndInt", function), () -> window().bitAndInt(function));
    }

    @Override
    public long bitAndLong(ToLongFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("bitAndLong", function), () -> window().bitAndLong(function));
    }

    @Override
    public Optional bitOr() {
        return partition.cacheIf(completePartition(), () -> tuple("bitOr"), () -> window().bitOr());
    }

    @Override
    public  Optional bitOr(Function function) {
        return partition.cacheIf(completePartition(), () -> tuple("bitOr", function), () -> window().bitOr(function));
    }

    @Override
    public int bitOrInt(ToIntFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("bitOrInt", function), () -> window().bitOrInt(function));
    }

    @Override
    public long bitOrLong(ToLongFunction function) {
        return partition.cacheIf(completePartition(), () -> tuple("bitOrLong", function), () -> window().bitOrLong(function));
    }

    @Override
    public  R collect(Collector collector) {
        return partition.cacheIf(completePartition(), () -> tuple("collect", collector), () -> window().collect(collector));
    }

    @Override
    public List toList() {
        return partition.cacheIf(completePartition(), "toList", () -> window().toList());
    }

    @Override
    public > L toList(Supplier factory) {
        return partition.cacheIf(completePartition(), () -> tuple("toList", factory), () -> window().toList(factory));
    }

    @Override
    public List toUnmodifiableList() {
        return Collections.unmodifiableList(toList());
    }

    @Override
    public Set toSet() {
        return partition.cacheIf(completePartition(), "toSet", () -> window().toSet());
    }

    @Override
    public > S toSet(Supplier factory) {
        return partition.cacheIf(completePartition(), () -> tuple("toSet", factory), () -> window().toSet(factory));
    }

    @Override
    public Set toUnmodifiableSet() {
        return Collections.unmodifiableSet(toSet());
    }

    @Override
    public > C toCollection(Supplier factory) {
        return partition.cacheIf(completePartition(), () -> tuple("toCollection", factory), () -> window().toCollection(factory));
    }

    @Override
    public  Map toMap(Function keyMapper, Function valueMapper) {
        return partition.cacheIf(completePartition(), () -> tuple("toMap", keyMapper, valueMapper), () -> window().toMap(keyMapper, valueMapper));
    }

    @Override
    public  Map toMap(Function keyMapper) {
        return toMap(keyMapper, Function.identity());
    }

    @Override
    public String toString() {
        return partition.cacheIf(completePartition(), "toString", () -> window().toString());
    }

    @Override
    public String toString(CharSequence delimiter) {
        return partition.cacheIf(completePartition(), () -> tuple("toString", delimiter), () -> Seq.toString(window(), delimiter));
    }

    @Override
    public String toString(CharSequence delimiter, CharSequence prefix, CharSequence suffix) {
        return partition.cacheIf(completePartition(), () -> tuple("toString", delimiter, prefix, suffix), () -> window().map(Objects::toString).collect(Collectors.joining(delimiter, prefix, suffix)));
    }

    @Override
    public String commonPrefix() {
        return partition.cacheIf(completePartition(), () -> "commonPrefix", () -> window().commonPrefix());
    }

    @Override
    public String commonSuffix() {
        return partition.cacheIf(completePartition(), () -> "commonSuffix", () -> window().commonSuffix());
    }
}