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

org.microbean.bean.Reducer Maven / Gradle / Ivy

The newest version!
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
 *
 * Copyright © 2024 microBean™.
 *
 * 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.microbean.bean;

import java.util.List;
import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

import java.util.function.BiFunction;

/**
 * A {@linkplain FunctionalInterface functional interface} whose implementations can either reduce a supplied
 * {@link List} of elements representing a successful selection to a single element normally drawn or
 * calculated from the selection according to some criteria, or fail gracefully in the face of ambiguity by
 * invoking a supplied failure handler.
 *
 * 

The reduction may be a simple filtering operation, or may be a summing or aggregating operation, or anything * else.

* *

This interface is conceptually subordinate to, but should not be confused with, the {@link Reducible} * interface.

* *

{@link Reducer} implementations are often used to help build {@link Reducible} implementations. See, for example, * {@link Reducible#ofCaching(Selectable, Reducer, BiFunction)}.

* * @param the type of criteria * * @param the element type * * @author Laird Nelson * * @see #reduce(List, Object, BiFunction) * * @see Reducible */ @FunctionalInterface public interface Reducer { /** * Performs some kind of reductive or filtering operation on the supplied {@link List}, according to the supplied * criteria, and returns the single result, or, if reduction fails, invokes the supplied {@link BiFunction} with a * sublist representing a partial reduction (or an empty list representing a reduction that simply could not be * performed), along with the supplied criteria, and returns its result. * *

Implementations of this method must return determinate values.

* * @param elements the {@link List} to reduce; must not be {@code null}; represents a successful selection from a * larger collection of elements * * @param c the criteria effectively describing the initial selection and the desired reduction; may be {@code null} * to indicate no criteria; may be ignored if not needed by an implementation * * @param failureHandler a {@link BiFunction} receiving a failed reduction (usually a portion of the supplied {@code * elements}), and the selection and reduction criteria, that returns a substitute reduction (or, more commonly, * throws an exception); must not be {@code null} * * @return a single, possibly {@code null}, element normally drawn or computed from the supplied {@code elements}, or * a synthetic value returned by an invocation of the supplied {@code failureHandler}'s {@link * BiFunction#apply(Object, Object)} method * * @exception NullPointerException if {@code elements} or {@code failureHandler} is {@code null} * * @exception ReductionException if the {@code failureHandler} function throws a {@link ReductionException} * * @see #fail(List, Object) */ // List, not Stream, for equality semantics and caching purposes. // List, not Set, because it's much faster and reduction can take care of duplicates if needed // List, not Collection, because you want easy access to the (possibly) only element without creating iterators // C, not Predicate, because it may not be necessary to actually filter the List to perform the reduction // failureHandler will receive only those elements that could not be eliminated // c is a pass-through used only during failure public T reduce(final List elements, final C c, final BiFunction, ? super C, ? extends T> failureHandler); /* * Default methods. */ public default T reduce(final Selectable f, final C c, final BiFunction, ? super C, ? extends T> failureHandler) { return this.reduce(f.select(c), c, failureHandler); } public default T reduce(final Selectable f, final C c) { return this.reduce(f.select(c), c, Reducer::fail); } public default T reduce(final List elements, final C c) { return this.reduce(elements, c, Reducer::fail); } // Experimental. Probably overkill. Unbounded. public default Reducer cached() { record Key(List l, C c) {}; final Map, T> cache = new ConcurrentHashMap<>(); return (l, c, fh) -> cache.computeIfAbsent(new Key(List.copyOf(l), c), k -> this.reduce(k.l(), k.c(), fh)); } /* * Static methods. */ // A Reducer that works only when the selection is of size 0 or 1. public static Reducer ofSimple() { return Reducer::reduceObviously; } // A Reducer that simply calls its supplied failure handler no matter what. public static Reducer ofFailing() { return Reducer::failUnconditionally; } // Default failure handler; call by method reference. Fails if the selection does not consist of one element. public static T fail(final List elements, final C c) { if (elements.isEmpty()) { throw new UnsatisfiedReductionException((Object)c); } else if (elements.size() > 1) { throw new AmbiguousReductionException(c, elements, "Cannot reduce: " + elements); } return elements.get(0); } // Convenience failure handler; call by method reference. Returns null when invoked. public static C returnNull(final A a, final B b) { return null; } private static T reduceObviously(final List l, final C c, final BiFunction, ? super C, ? extends T> fh) { return l.isEmpty() ? null : l.size() == 1 ? l.get(0) : fh.apply(l, c); } private static T failUnconditionally(final List l, final C c, final BiFunction, ? super C, ? extends T> fh) { return fh.apply(l, c); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy