@Override
  public void writeTo(StreamOutput out) throws IOException {
    this.getShard().writeTo(out);
    out.writeBoolean(this.isPrimary());
    out.writeBoolean(this.isStillFetchingShardData());
    out.writeOptionalString(this.getAssignedNodeId());
    out.writeOptionalWriteable(this.getUnassignedInfo());
    out.writeVLong(allocationDelayMillis);
    out.writeVLong(remainingDelayMillis);

    out.writeVInt(this.nodeExplanations.size());
    for (NodeExplanation explanation : this.nodeExplanations.values()) {
      explanation.writeTo(out);
    }
  }
  public ClusterAllocationExplanation(StreamInput in) throws IOException {
    this.shard = ShardId.readShardId(in);
    this.primary = in.readBoolean();
    this.hasPendingAsyncFetch = in.readBoolean();
    this.assignedNodeId = in.readOptionalString();
    this.unassignedInfo = in.readOptionalWriteable(UnassignedInfo::new);
    this.allocationDelayMillis = in.readVLong();
    this.remainingDelayMillis = in.readVLong();

    int mapSize = in.readVInt();
    Map<DiscoveryNode, NodeExplanation> nodeToExplanation = new HashMap<>(mapSize);
    for (int i = 0; i < mapSize; i++) {
      NodeExplanation nodeExplanation = new NodeExplanation(in);
      nodeToExplanation.put(nodeExplanation.getNode(), nodeExplanation);
    }
    this.nodeExplanations = nodeToExplanation;
  }
 public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
   builder.startObject();
   {
     builder.startObject("shard");
     {
       builder.field("index", shard.getIndexName());
       builder.field("index_uuid", shard.getIndex().getUUID());
       builder.field("id", shard.getId());
       builder.field("primary", primary);
     }
     builder.endObject(); // end shard
     builder.field("assigned", this.assignedNodeId != null);
     // If assigned, show the node id of the node it's assigned to
     if (assignedNodeId != null) {
       builder.field("assigned_node_id", this.assignedNodeId);
     }
     builder.field("shard_state_fetch_pending", this.hasPendingAsyncFetch);
     // If we have unassigned info, show that
     if (unassignedInfo != null) {
       unassignedInfo.toXContent(builder, params);
       builder.timeValueField(
           "allocation_delay_in_millis",
           "allocation_delay",
           TimeValue.timeValueMillis(allocationDelayMillis));
       builder.timeValueField(
           "remaining_delay_in_millis",
           "remaining_delay",
           TimeValue.timeValueMillis(remainingDelayMillis));
     }
     builder.startObject("nodes");
     for (NodeExplanation explanation : nodeExplanations.values()) {
       explanation.toXContent(builder, params);
     }
     builder.endObject(); // end nodes
   }
   builder.endObject(); // end wrapping object
   return builder;
 }