protected GenerateOutput generateResultProjection() {
        JoinedRow joinedRow = new JoinedRow();
        List<Attribute> bufferAttributes = flatMapToList(aggregateFunctions, AggregateFunction::aggBufferAttributes);
        if (modes.contains(AggregateMode.Final) || modes.contains(AggregateMode.Complete)) {
            List<Expression> evalExpressions = mapToList(aggregateFunctions, AggregateFunction::evaluateExpression);
            UnsafeRow aggregateResult = UnsafeRow.create8BytesFieldsRow(aggregateAttributes.size());

            MutableProjection expressionAggEvalProjection = newMutableProjection.apply(evalExpressions, bufferAttributes).get();
            expressionAggEvalProjection.target(aggregateResult);

            UnsafeProjection resultProjection = UnsafeProjection.create(
                    resultExpressions,
                    concatToList(groupingAttributes, aggregateAttributes));

            return (currentGroupingKey, currentBuffer) -> {
                expressionAggEvalProjection.apply(currentBuffer);
                return resultProjection.apply(joinedRow.apply(currentGroupingKey, aggregateResult));
            };
        } else if (modes.contains(AggregateMode.Partial) || modes.contains(AggregateMode.PartialMerge)) {
            UnsafeProjection resultProjection = UnsafeProjection.create(
                    concatToList(groupingAttributes, bufferAttributes),
                    concatToList(groupingAttributes, bufferAttributes)
            );
            return (currentGroupingKey, currentBuffer) -> {
                return resultProjection.apply(joinedRow.apply(currentGroupingKey, currentBuffer));
            };
        } else {
            // Grouping-only: we only output values of grouping expressions.
            UnsafeProjection resultProjection = UnsafeProjection.create(resultExpressions, groupingAttributes);
            return (currentGroupingKey, currentBuffer) -> {
                return resultProjection.apply(currentGroupingKey);
            };
        }
    }
The base class of [[SortBasedAggregationIterator]] and [[TungstenAggregationIterator]].
It mainly contains two parts:
1. It initializes aggregate functions.
2. It creates two functions, `processRow` and `generateOutput` based on [[AggregateMode]] of
its aggregate functions. `processRow` is the function to handle an input. `generateOutput`
is used to generate result.
indexr-query-opt/src/main/java/org/apache/spark/sql/execution/aggregate/AggregationIterator.java