public ConsumerGroupMember build() {
        switch (member.state()) {
            case STABLE:
                // When the member is in the STABLE state, we verify if a newer
                // epoch (or target assignment) is available. If it is, we can
                // reconcile the member towards it. Otherwise, we return.
                if (member.memberEpoch() != targetAssignmentEpoch) {
                    return computeNextAssignment(
                        member.memberEpoch(),
                        member.assignedPartitions()
                    );
                } else {
                    return member;
                }

            case UNREVOKED_PARTITIONS:
                // When the member is in the UNREVOKED_PARTITIONS state, we wait
                // until the member has revoked the necessary partitions. They are
                // considered revoked when they are not anymore reported in the
                // owned partitions set in the ConsumerGroupHeartbeat API.

                // If the member provides its owned partitions. We verify if it still
                // owns any of the revoked partitions. If it does, we cannot progress.
                if (ownsRevokedPartitions(member.partitionsPendingRevocation())) {
                    return member;
                }

                // When the member has revoked all the pending partitions, it can
                // transition to the next epoch (current + 1) and we can reconcile
                // its state towards the latest target assignment.
                return computeNextAssignment(
                    member.memberEpoch() + 1,
                    member.assignedPartitions()
                );

            case UNRELEASED_PARTITIONS:
                // When the member is in the UNRELEASED_PARTITIONS, we reconcile the
                // member towards the latest target assignment. This will assign any
                // of the unreleased partitions when they become available.
                return computeNextAssignment(
                    member.memberEpoch(),
                    member.assignedPartitions()
                );

            case UNKNOWN:
                // We could only end up in this state if a new state is added in the
                // future and the group coordinator is downgraded. In this case, the
                // best option is to fence the member to force it to rejoin the group
                // without any partitions and to reconcile it again from scratch.
                if (ownedTopicPartitions == null || !ownedTopicPartitions.isEmpty()) {
                    throw new FencedMemberEpochException("The consumer group member is in a unknown state. "
                        + "The member must abandon all its partitions and rejoin.");
                }

                return computeNextAssignment(
                    targetAssignmentEpoch,
                    member.assignedPartitions()
                );
        }

        return member;
    }
Builds the next state for the member or keep the current one if it
is not possible to move forward with the current state.

@return A new ConsumerGroupMember or the current one.
group-coordinator/src/main/java/org/apache/kafka/coordinator/group/modern/consumer/CurrentAssignmentBuilder.java