class ExecutionPhases {

    /**
     * @return true if the executionNodes indicate a execution on the handler node.
     *
     * size == 0 is also true
     * (this currently is the case on MergePhases if there is a direct-response from previous phase to MergePhase)
     */
    public static boolean executesOnHandler(String handlerNode, Collection<String> executionNodes) {
        switch (executionNodes.size()) {
            case 0:
                return true;
            case 1:
                return executionNodes.iterator().next().equals(handlerNode);
            default:
                return false;
        }
    }

    public static ExecutionPhase fromStream(StreamInput in) throws IOException {
        return ExecutionPhase.Type.VALUES.get(in.readVInt()).fromStream(in);
    }

    public static void toStream(StreamOutput out, ExecutionPhase node) throws IOException {
        out.writeVInt(node.type().ordinal());
        node.writeTo(out);
    }

    public static boolean hasDirectResponseDownstream(Collection<String> downstreamNodes) {
        for (String nodeId : downstreamNodes) {
            if (nodeId.equals(ExecutionPhase.DIRECT_RESPONSE)) {
                return true;
            }
        }
        return false;
    }

    public static String debugPrint(ExecutionPhase phase) {<FILL_FUNCTION_BODY>}
}

        StringBuilder sb = new StringBuilder("phase{id=");
        sb.append(phase.phaseId());
        sb.append("/");
        sb.append(phase.name());
        sb.append(", ");
        sb.append("nodes=");
        sb.append(phase.nodeIds());
        if (phase instanceof UpstreamPhase) {
            UpstreamPhase uPhase = (UpstreamPhase) phase;
            sb.append(", dist=");
            sb.append(uPhase.distributionInfo().distributionType());
        }
        sb.append("}");
        return sb.toString();
    
<no_super_class>