class DecommissionAllocationDecider extends AllocationDecider {

    public static final String NAME = "decommission";

    private Set<String> decommissioningNodes = Set.of();

    private DataAvailability dataAvailability;

    public DecommissionAllocationDecider(Settings settings, ClusterSettings clusterSettings) {
        updateDecommissioningNodes(DecommissioningService.DECOMMISSION_INTERNAL_SETTING_GROUP.get(settings));
        dataAvailability = DecommissioningService.GRACEFUL_STOP_MIN_AVAILABILITY_SETTING.get(settings);

        clusterSettings.addSettingsUpdateConsumer(
            DecommissioningService.DECOMMISSION_INTERNAL_SETTING_GROUP, this::updateDecommissioningNodes);
        clusterSettings.addSettingsUpdateConsumer(
            DecommissioningService.GRACEFUL_STOP_MIN_AVAILABILITY_SETTING, this::updateMinAvailability);
    }

    private void updateDecommissioningNodes(Settings decommissionNodesSettings) {
        decommissioningNodes = decommissionNodesSettings.keySet();
    }

    private void updateMinAvailability(DataAvailability availability) {
        dataAvailability = availability;
    }

    @Override
    public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {<FILL_FUNCTION_BODY>}

    @Override
    public Decision canRemain(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {
        return canRemainOrAllocate(node.nodeId(), shardRouting, allocation);
    }

    private Decision canRemainOrAllocate(String nodeId, ShardRouting shardRouting, RoutingAllocation allocation) {
        if (dataAvailability == DataAvailability.NONE) {
            return allocation.decision(Decision.YES, NAME, "dataAvailability=none");
        }

        if (decommissioningNodes.contains(nodeId)) {
            if (dataAvailability == DataAvailability.PRIMARIES && !shardRouting.primary()) {
                return allocation.decision(Decision.YES, NAME, "dataAvailability=primaries shard=replica decommissioned=true");
            }
            return allocation.decision(Decision.NO, NAME, "node is being decommissioned");
        }
        return allocation.decision(Decision.YES, NAME, "node isn't decommissioned");
    }
}

        if (decommissioningNodes.contains(node.nodeId())
            && dataAvailability == DataAvailability.PRIMARIES
            && !shardRouting.primary()) {

            // if primaries are removed from this node it will try to re-balance non-primaries onto this node
            // prevent this - replicas that are already here can remain, but no new replicas should be assigned
            return allocation.decision(Decision.NO, NAME, "dataAvailability=primaries, shard=replica, decommissioned=true");
        }
        return canRemainOrAllocate(node.nodeId(), shardRouting, allocation);
    
<methods>public org.elasticsearch.cluster.routing.allocation.decider.Decision canAllocate(org.elasticsearch.cluster.routing.ShardRouting, org.elasticsearch.cluster.routing.RoutingNode, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision canAllocate(org.elasticsearch.cluster.routing.ShardRouting, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision canAllocate(org.elasticsearch.cluster.metadata.IndexMetadata, org.elasticsearch.cluster.routing.RoutingNode, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision canForceAllocatePrimary(org.elasticsearch.cluster.routing.ShardRouting, org.elasticsearch.cluster.routing.RoutingNode, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision canRebalance(org.elasticsearch.cluster.routing.ShardRouting, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision canRebalance(org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision canRemain(org.elasticsearch.cluster.routing.ShardRouting, org.elasticsearch.cluster.routing.RoutingNode, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) ,public org.elasticsearch.cluster.routing.allocation.decider.Decision shouldAutoExpandToNode(org.elasticsearch.cluster.metadata.IndexMetadata, org.elasticsearch.cluster.node.DiscoveryNode, org.elasticsearch.cluster.routing.allocation.RoutingAllocation) <variables>