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

com.hazelcast.jet.aggregate.AggregateOperationBuilder Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.jet.aggregate;

import com.hazelcast.function.BiConsumerEx;
import com.hazelcast.function.FunctionEx;
import com.hazelcast.function.SupplierEx;
import com.hazelcast.jet.core.Processor;
import com.hazelcast.jet.datamodel.Tag;
import com.hazelcast.jet.impl.aggregate.AggregateOperation1Impl;
import com.hazelcast.jet.impl.aggregate.AggregateOperation2Impl;
import com.hazelcast.jet.impl.aggregate.AggregateOperation3Impl;
import com.hazelcast.jet.impl.aggregate.AggregateOperationImpl;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;

import static com.hazelcast.internal.serialization.impl.SerializationUtil.checkSerializable;
import static java.util.stream.Collectors.toList;
import static java.util.stream.IntStream.range;

/**
 * A builder object that can be used to construct the definition of an
 * aggregate operation in a step-by-step manner. Please refer to
 * {@link AggregateOperation#withCreate(SupplierEx)
 * AggregateOperation.withCreate()} for more details.
 *
 * @param  the type of the accumulator
 *
 * @since Jet 3.0
 */
public final class AggregateOperationBuilder {

    @Nonnull
    private final SupplierEx createFn;

    AggregateOperationBuilder(@Nonnull SupplierEx createFn) {
        this.createFn = createFn;
    }

    /**
     * Registers the {@link AggregateOperation1#accumulateFn()} accumulate}
     * primitive. Also selects the fixed-arity variant of the aggregate
     * operation.
     * 

* This method is synonymous with {@link #andAccumulate0(BiConsumerEx) * andAccumulate0()}, but makes more sense when defining a simple, arity-1 * aggregate operation. * * @param accumulateFn the {@code accumulate} primitive, parameters are * {@code (accumulator, item)}. It must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * @param the expected type of input item * @return a new builder object that captures the {@code T0} type parameter */ @Nonnull public Arity1 andAccumulate(@Nonnull BiConsumerEx accumulateFn) { checkSerializable(accumulateFn, "accumulateFn"); return new Arity1<>(createFn, accumulateFn); } /** * Registers the {@link AggregateOperation1#accumulateFn() accumulate} * primitive for stream-0. Also selects the fixed-arity variant of the * aggregate operation. * * @param accumulateFn0 the {@code accumulate} primitive for stream-0. It * must be stateless and {@linkplain Processor#isCooperative() cooperative}. * @param the expected type of item in stream-0 * @return a new builder object that captures the {@code T0} type parameter */ @Nonnull public Arity1 andAccumulate0(@Nonnull BiConsumerEx accumulateFn0) { checkSerializable(accumulateFn0, "accumulateFn0"); return new Arity1<>(createFn, accumulateFn0); } /** * Selects the variable-arity variant for this aggregate operation builder. * * @return a new builder object for variable-arity aggregate operations which has * the {@code createFn} of the current builder */ public VarArity varArity() { return new VarArity<>(createFn); } /** * Registers the {@link AggregateOperation#accumulateFn(Tag) accumulate} * primitive for the stream tagged with the supplied tag. Also selects the * variable-arity variant of the aggregate operation. * * @param tag the tag of the associated input stream * @param accumulateFn the {@code accumulate} primitive. It must be * stateless and {@linkplain Processor#isCooperative() cooperative}. * @param the expected type of input item * @return a new builder object for variable-arity aggregate operations which has * the {@code createFn} of the current builder */ @Nonnull public VarArity andAccumulate( @Nonnull Tag tag, @Nonnull BiConsumerEx accumulateFn ) { checkSerializable(accumulateFn, "accumulateFn"); return new VarArity<>(createFn, tag, accumulateFn); } /** * The arity-1 variant of the aggregate operation builder. Can be raised to * arity-2 by calling {@link #andAccumulate1(BiConsumerEx) andAccumulate1()}. * * @param type of item in stream-0 * @param type of the accumulator * @param type of the aggregation result */ public static class Arity1 { @Nonnull private final SupplierEx createFn; @Nonnull private final BiConsumerEx accumulateFn0; private BiConsumerEx combineFn; private BiConsumerEx deductFn; private FunctionEx exportFn; Arity1( @Nonnull SupplierEx createFn, @Nonnull BiConsumerEx accumulateFn0 ) { this.createFn = createFn; this.accumulateFn0 = accumulateFn0; } /** * Registers the {@link AggregateOperation2#accumulateFn1()} accumulate} * primitive for stream-1, returning the arity-2 variant of the builder. * * @param accumulateFn1 the {@code accumulate} primitive for stream-1. It * must be stateless and {@linkplain Processor#isCooperative() cooperative}. * @param the expected type of item in stream-1 * @return a new builder object that captures the {@code T1} type parameter */ @Nonnull public Arity2 andAccumulate1( @Nonnull BiConsumerEx accumulateFn1 ) { checkSerializable(accumulateFn1, "accumulateFn1"); return new Arity2<>(this, accumulateFn1); } /** * Registers the {@link AggregateOperation#combineFn() combine} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity1 andCombine(@Nullable BiConsumerEx combineFn) { checkSerializable(combineFn, "combineFn"); this.combineFn = combineFn; return this; } /** * Registers the {@link AggregateOperation#deductFn() deduct} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity1 andDeduct(@Nullable BiConsumerEx deductFn) { checkSerializable(deductFn, "deductFn"); this.deductFn = deductFn; return this; } /** * Registers the {@link AggregateOperation#exportFn() export} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity1 andExport( @Nonnull FunctionEx exportFn ) { checkSerializable(exportFn, "exportFn"); @SuppressWarnings("unchecked") Arity1 newThis = (Arity1) this; newThis.exportFn = exportFn; return newThis; } /** * Registers the {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation1} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive was * not registered */ @Nonnull public AggregateOperation1 andFinish( @Nonnull FunctionEx finishFn ) { if (exportFn == null) { throw new IllegalStateException( "The export primitive is not registered. Either add the missing andExport() call" + " or use andExportFinish() to register the same function as both the export and" + " finish primitive"); } checkSerializable(finishFn, "finishFn"); return new AggregateOperation1Impl<>( createFn, accumulateFn0, combineFn, deductFn, exportFn, finishFn); } /** * Registers the supplied function as both the {@link AggregateOperation#exportFn() * export} and {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation1} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive is * already registered */ @Nonnull public AggregateOperation1 andExportFinish( @Nonnull FunctionEx exportFinishFn ) { if (exportFn != null) { throw new IllegalStateException("The export primitive is already registered. Call" + " andFinish() if you want to register a separate finish primitive."); } checkSerializable(exportFinishFn, "exportFinishFn"); return new AggregateOperation1Impl<>( createFn, accumulateFn0, combineFn, deductFn, exportFinishFn, exportFinishFn); } } /** * The arity-2 variant of the aggregate operation builder. Can be raised to * arity-3 by calling {@link #andAccumulate2(BiConsumerEx) andAccumulate2()}. * * @param the type of item in stream-0 * @param the type of item in stream-1 * @param the type of the accumulator * @param type of the aggregation result */ public static class Arity2 { @Nonnull private final SupplierEx createFn; @Nonnull private final BiConsumerEx accumulateFn0; @Nonnull private final BiConsumerEx accumulateFn1; private BiConsumerEx combineFn; private BiConsumerEx deductFn; private FunctionEx exportFn; Arity2(@Nonnull Arity1 step1, @Nonnull BiConsumerEx accumulateFn1) { this.createFn = step1.createFn; this.accumulateFn0 = step1.accumulateFn0; this.accumulateFn1 = accumulateFn1; } /** * Registers the {@link AggregateOperation3#accumulateFn2() accumulate} * primitive for stream-2, returning the arity-3 variant of the builder. * * @param accumulateFn2 the {@code accumulate} primitive for stream-2. It * must be stateless and {@linkplain Processor#isCooperative() cooperative}. * @param the expected type of item in stream-2 * @return a new builder object that captures the {@code T2} type parameter */ @Nonnull public Arity3 andAccumulate2( @Nonnull BiConsumerEx accumulateFn2 ) { checkSerializable(accumulateFn2, "accumulateFn2"); return new Arity3<>(this, accumulateFn2); } /** * Registers the {@link AggregateOperation#combineFn() combine} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity2 andCombine(@Nullable BiConsumerEx combineFn) { checkSerializable(combineFn, "combineFn"); this.combineFn = combineFn; return this; } /** * Registers the {@link AggregateOperation#deductFn() deduct} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity2 andDeduct(@Nullable BiConsumerEx deductFn) { checkSerializable(deductFn, "deductFn"); this.deductFn = deductFn; return this; } /** * Registers the {@link AggregateOperation#exportFn() export} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity2 andExport( @Nonnull FunctionEx exportFn ) { checkSerializable(exportFn, "exportFn"); @SuppressWarnings("unchecked") Arity2 newThis = (Arity2) this; newThis.exportFn = exportFn; return newThis; } /** * Registers the {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation2} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive was * not registered */ @Nonnull public AggregateOperation2 andFinish( @Nonnull FunctionEx finishFn ) { checkSerializable(finishFn, "finishFn"); if (exportFn == null) { throw new IllegalStateException( "The export primitive is not registered. Either add the missing andExport() call" + " or use andExportFinish() to register the same function as both the export and" + " finish primitive"); } return new AggregateOperation2Impl<>( createFn, accumulateFn0, accumulateFn1, combineFn, deductFn, exportFn, finishFn); } /** * Registers the supplied function as both the {@link AggregateOperation#exportFn() * export} and {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation2} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive is * already registered */ @Nonnull public AggregateOperation2 andExportFinish( @Nonnull FunctionEx exportFinishFn ) { return new AggregateOperation2Impl<>(createFn, accumulateFn0, accumulateFn1, combineFn, deductFn, exportFinishFn, exportFinishFn); } } /** * The arity-3 variant of the aggregate operation builder. * * @param the type of item in stream-0 * @param the type of item in stream-1 * @param the type of item in stream-2 * @param the type of the accumulator * @param type of the aggregation result */ public static class Arity3 { @Nonnull private final SupplierEx createFn; @Nonnull private final BiConsumerEx accumulateFn0; @Nonnull private final BiConsumerEx accumulateFn1; @Nonnull private final BiConsumerEx accumulateFn2; private BiConsumerEx combineFn; private BiConsumerEx deductFn; private FunctionEx exportFn; Arity3(Arity2 arity2, @Nonnull BiConsumerEx accumulateFn2) { this.createFn = arity2.createFn; this.accumulateFn0 = arity2.accumulateFn0; this.accumulateFn1 = arity2.accumulateFn1; this.accumulateFn2 = accumulateFn2; } /** * Registers the {@link AggregateOperation#combineFn() combine} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity3 andCombine(@Nullable BiConsumerEx combineFn) { checkSerializable(combineFn, "combineFn"); this.combineFn = combineFn; return this; } /** * Registers the {@link AggregateOperation#deductFn() deduct} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity3 andDeduct(@Nullable BiConsumerEx deductFn) { checkSerializable(deductFn, "deductFn"); this.deductFn = deductFn; return this; } /** * Registers the {@link AggregateOperation#exportFn() export} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public Arity3 andExport( @Nonnull FunctionEx exportFn ) { checkSerializable(exportFn, "exportFn"); @SuppressWarnings("unchecked") Arity3 newThis = (Arity3) this; newThis.exportFn = exportFn; return newThis; } /** * Registers the {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation3} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive was * not registered */ @Nonnull public AggregateOperation3 andFinish( @Nonnull FunctionEx finishFn ) { if (exportFn == null) { throw new IllegalStateException( "The export primitive is not registered. Either add the missing andExport() call" + " or use andExportFinish() to register the same function as both the export and" + " finish primitive"); } checkSerializable(finishFn, "finishFn"); return new AggregateOperation3Impl<>(createFn, accumulateFn0, accumulateFn1, accumulateFn2, combineFn, deductFn, exportFn, finishFn); } /** * Registers the supplied function as both the {@link AggregateOperation#exportFn() * export} and {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation3} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive is * already registered */ @Nonnull public AggregateOperation3 andExportFinish( @Nonnull FunctionEx exportFinishFn ) { checkSerializable(exportFinishFn, "exportFinishFn"); return new AggregateOperation3Impl<>(createFn, accumulateFn0, accumulateFn1, accumulateFn2, combineFn, deductFn, exportFinishFn, exportFinishFn); } } /** * The variable-arity variant of the aggregate operation builder. * Accepts any number of {@code accumulate} primitives and associates * them with {@link Tag}s. Does not capture the static type of the * stream items. * * @param the type of the accumulator * @param type of the aggregation result */ public static class VarArity { @Nonnull private final SupplierEx createFn; private final Map> accumulateFnsByTag = new HashMap<>(); private BiConsumerEx combineFn; private BiConsumerEx deductFn; private FunctionEx exportFn; VarArity(@Nonnull SupplierEx createFn) { this.createFn = createFn; } VarArity( @Nonnull SupplierEx createFn, @Nonnull Tag tag, @Nonnull BiConsumerEx accumulateFn ) { this(createFn); accumulateFnsByTag.put(tag.index(), accumulateFn); } /** * Registers the {@link AggregateOperation#accumulateFn(Tag) accumulate} * primitive for the stream tagged with the supplied tag. * * @param tag the tag of the associated input stream * @param accumulateFn the {@code accumulate} primitive. It must be * stateless and {@linkplain Processor#isCooperative() cooperative}. * @param the expected type of input item * @return this */ @Nonnull public VarArity andAccumulate( @Nonnull Tag tag, @Nonnull BiConsumerEx accumulateFn ) { checkSerializable(accumulateFn, "accumulateFn"); if (accumulateFnsByTag.putIfAbsent(tag.index(), accumulateFn) != null) { throw new IllegalArgumentException("Tag with index " + tag.index() + " already registered"); } return this; } /** * Registers the {@link AggregateOperation#combineFn() combine} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public VarArity andCombine(@Nullable BiConsumerEx combineFn) { checkSerializable(combineFn, "combineFn"); this.combineFn = combineFn; return this; } /** * Registers the {@link AggregateOperation#deductFn() deduct} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public VarArity andDeduct(@Nullable BiConsumerEx deductFn) { checkSerializable(deductFn, "deductFn"); this.deductFn = deductFn; return this; } /** * Registers the {@link AggregateOperation#exportFn() export} primitive. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. */ @Nonnull public VarArity andExport( @Nonnull FunctionEx exportFn ) { checkSerializable(exportFn, "exportFn"); @SuppressWarnings("unchecked") VarArity newThis = (VarArity) this; newThis.exportFn = exportFn; return newThis; } /** * Registers the {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive was * not registered */ @Nonnull public AggregateOperation andFinish( @Nonnull FunctionEx finishFn ) { if (exportFn == null) { throw new IllegalStateException( "The export primitive is not registered. Either add the missing andExport() call" + " or use andExportFinish() to register the same function as both the export and" + " finish primitive"); } checkSerializable(finishFn, "finishFn"); return new AggregateOperationImpl<>( createFn, packAccumulateFns(), combineFn, deductFn, exportFn, finishFn); } /** * Registers the supplied function as both the {@link AggregateOperation#exportFn() * export} and {@link AggregateOperation#finishFn() finish} primitive. * Constructs and returns an {@link AggregateOperation} from the current * state of the builder. *

* The given function must be stateless and {@linkplain * Processor#isCooperative() cooperative}. * * @throws IllegalStateException if the {@code export} primitive is * already registered */ @Nonnull public AggregateOperation andExportFinish( @Nonnull FunctionEx exportFinishFn ) { if (exportFn != null) { throw new IllegalStateException("The export primitive is already registered. Call" + " andFinish() if you want to register a separate finish primitive."); } checkSerializable(exportFinishFn, "exportFinishFn"); return new AggregateOperationImpl<>( createFn, packAccumulateFns(), combineFn, deductFn, exportFinishFn, exportFinishFn); } private BiConsumerEx[] packAccumulateFns() { int size = accumulateFnsByTag.size(); @SuppressWarnings("unchecked") BiConsumerEx[] accFns = new BiConsumerEx[size]; for (int i = 0; i < size; i++) { accFns[i] = accumulateFnsByTag.get(i); if (accFns[i] == null) { throw new IllegalStateException("Registered tags' indices are " + accumulateFnsByTag.keySet().stream().sorted().collect(toList()) + " but should be " + range(0, size).boxed().collect(toList())); } } return accFns; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy