CompositeBuilderImpl.java

package sprouts.impl;

import org.jspecify.annotations.Nullable;
import sprouts.Tuple;
import sprouts.Val;
import sprouts.Viewable;

import java.util.Objects;
import java.util.function.BiFunction;

/**
 *  The implementation of the {@link Viewable.CompositeBuilder} interface, which collects the
 *  properties a composite view is folded from, together with their combiners.
 *  <p>
 *  This is a persistent value: {@link #join(Val, BiFunction)} does not modify the builder it is
 *  called on, but returns a new one carrying one additional {@link CompositeCore.Join}.
 *  The joins are collected in a {@link Tuple}, whose structural sharing is what keeps a chain of
 *  {@code join(..)} calls from copying everything which was already declared, over and over again.
 *  <p>
 *  Nothing is registered on the joined properties here, which is what makes an instance of this
 *  inert once it escapes the configurator function it was handed to.
 *
 * @param <C> The item type of the composite view being built.
 */
final class CompositeBuilderImpl<C> implements Viewable.CompositeBuilder<C> {

    private static final CompositeBuilderImpl<?> EMPTY = new CompositeBuilderImpl<>(_noJoins());

    @SuppressWarnings("unchecked")
    static <C> CompositeBuilderImpl<C> empty() {
        return (CompositeBuilderImpl<C>) EMPTY;
    }

    /**
     *  The item type of the tuple of joins is generic, which a {@link Class} can never express,
     *  so the raw {@link CompositeCore.Join} class is the most precise type we can hand to the
     *  tuple. It only ever uses it to type check the items it is given, and every join is an
     *  instance of that raw type, no matter its type arguments.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private static <C> Tuple<CompositeCore.Join<C, ?>> _noJoins() {
        return (Tuple) Tuple.of(CompositeCore.Join.class);
    }

    private final Tuple<CompositeCore.Join<C, ?>> _joins;

    private CompositeBuilderImpl( Tuple<CompositeCore.Join<C, ?>> joins ) {
        _joins = joins;
    }

    @Override
    public <V extends @Nullable Object> Viewable.CompositeBuilder<C> join(
        Val<V>              property,
        BiFunction<C, V, C> combiner
    ) {
        Objects.requireNonNull(property, "The property to join must not be null.");
        Objects.requireNonNull(combiner, "The combiner of a joined property must not be null.");
        return new CompositeBuilderImpl<>(_joins.add(new CompositeCore.Join<>(property, combiner)));
    }

    Tuple<CompositeCore.Join<C, ?>> joins() {
        return _joins;
    }
}