/* * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ package tools.refinery.store.query.term; import java.util.Comparator; import java.util.Objects; import java.util.SortedMap; import java.util.TreeMap; public class ExtremeValueAggregator implements StatefulAggregator { private final Class type; private final T emptyResult; private final Comparator comparator; public ExtremeValueAggregator(Class type, T emptyResult) { this(type, emptyResult, null); } public ExtremeValueAggregator(Class type, T emptyResult, Comparator comparator) { this.type = type; this.emptyResult = emptyResult; this.comparator = comparator; } @Override public Class getResultType() { return getInputType(); } @Override public Class getInputType() { return type; } @Override public StatefulAggregate createEmptyAggregate() { return new Aggregate(); } @Override public T getEmptyResult() { return emptyResult; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ExtremeValueAggregator that = (ExtremeValueAggregator) o; return type.equals(that.type) && Objects.equals(emptyResult, that.emptyResult) && Objects.equals(comparator, that.comparator); } @Override public int hashCode() { return Objects.hash(type, emptyResult, comparator); } private class Aggregate implements StatefulAggregate { private final SortedMap values; private Aggregate() { values = new TreeMap<>(comparator); } private Aggregate(Aggregate other) { values = new TreeMap<>(other.values); } @Override public void add(T value) { values.compute(value, (ignoredValue, currentCount) -> currentCount == null ? 1 : currentCount + 1); } @Override public void remove(T value) { values.compute(value, (theValue, currentCount) -> { if (currentCount == null || currentCount <= 0) { throw new IllegalStateException("Invalid count %d for value %s".formatted(currentCount, theValue)); } return currentCount.equals(1) ? null : currentCount - 1; }); } @Override public T getResult() { return isEmpty() ? emptyResult : values.firstKey(); } @Override public boolean isEmpty() { return values.isEmpty(); } @Override public StatefulAggregate deepCopy() { return new Aggregate(this); } @Override public boolean contains(T value) { return StatefulAggregate.super.contains(value); } } }