public Builder(String index) {
   this.index = index;
   this.mappings = ImmutableOpenMap.builder();
   this.aliases = ImmutableOpenMap.builder();
   this.customs = ImmutableOpenMap.builder();
   this.activeAllocationIds = ImmutableOpenIntMap.builder();
 }
 /**
  * A group shards iterator where each group ({@link ShardIterator} is an iterator across shard
  * replication group.
  */
 public GroupShardsIterator groupByShardsIt() {
   // use list here since we need to maintain identity across shards
   ArrayList<ShardIterator> set = new ArrayList<ShardIterator>(shards.size());
   for (IndexShardRoutingTable indexShard : this) {
     set.add(indexShard.shardsIt());
   }
   return new GroupShardsIterator(set);
 }
 public Builder(IndexMetaData indexMetaData) {
   this.index = indexMetaData.getIndex().getName();
   this.state = indexMetaData.state;
   this.version = indexMetaData.version;
   this.settings = indexMetaData.getSettings();
   this.mappings = ImmutableOpenMap.builder(indexMetaData.mappings);
   this.aliases = ImmutableOpenMap.builder(indexMetaData.aliases);
   this.customs = ImmutableOpenMap.builder(indexMetaData.customs);
   this.activeAllocationIds = ImmutableOpenIntMap.builder(indexMetaData.activeAllocationIds);
 }
  public static class Builder {

    private final String index;
    private final ImmutableOpenIntMap.Builder<IndexShardRoutingTable> shards =
        ImmutableOpenIntMap.builder();

    public Builder(String index) {
      this.index = index;
    }

    /**
     * Reads an {@link IndexRoutingTable} from an {@link StreamInput}
     *
     * @param in {@link StreamInput} to read the {@link IndexRoutingTable} from
     * @return {@link IndexRoutingTable} read
     * @throws IOException if something happens during read
     */
    public static IndexRoutingTable readFrom(StreamInput in) throws IOException {
      String index = in.readString();
      Builder builder = new Builder(index);

      int size = in.readVInt();
      for (int i = 0; i < size; i++) {
        builder.addIndexShard(IndexShardRoutingTable.Builder.readFromThin(in, index));
      }

      return builder.build();
    }

    /**
     * Writes an {@link IndexRoutingTable} to a {@link StreamOutput}.
     *
     * @param index {@link IndexRoutingTable} to write
     * @param out {@link StreamOutput} to write to
     * @throws IOException if something happens during write
     */
    public static void writeTo(IndexRoutingTable index, StreamOutput out) throws IOException {
      out.writeString(index.index());
      out.writeVInt(index.shards.size());
      for (IndexShardRoutingTable indexShard : index) {
        IndexShardRoutingTable.Builder.writeToThin(indexShard, out);
      }
    }

    /** Initializes a new empty index, as if it was created from an API. */
    public Builder initializeAsNew(IndexMetaData indexMetaData) {
      return initializeEmpty(indexMetaData, true);
    }

    /** Initializes a new empty index, as if it was created from an API. */
    public Builder initializeAsRecovery(IndexMetaData indexMetaData) {
      return initializeEmpty(indexMetaData, false);
    }

    /** Initializes a new empty index, to be restored from a snapshot */
    public Builder initializeAsNewRestore(
        IndexMetaData indexMetaData, RestoreSource restoreSource) {
      return initializeAsRestore(indexMetaData, restoreSource, true);
    }

    /** Initializes an existing index, to be restored from a snapshot */
    public Builder initializeAsRestore(IndexMetaData indexMetaData, RestoreSource restoreSource) {
      return initializeAsRestore(indexMetaData, restoreSource, false);
    }

    /** Initializes an index, to be restored from snapshot */
    private Builder initializeAsRestore(
        IndexMetaData indexMetaData, RestoreSource restoreSource, boolean asNew) {
      if (!shards.isEmpty()) {
        throw new ElasticSearchIllegalStateException(
            "trying to initialize an index with fresh shards, but already has shards created");
      }
      for (int shardId = 0; shardId < indexMetaData.numberOfShards(); shardId++) {
        IndexShardRoutingTable.Builder indexShardRoutingBuilder =
            new IndexShardRoutingTable.Builder(
                new ShardId(indexMetaData.index(), shardId), asNew ? false : true);
        for (int i = 0; i <= indexMetaData.numberOfReplicas(); i++) {
          indexShardRoutingBuilder.addShard(
              new ImmutableShardRouting(
                  index,
                  shardId,
                  null,
                  null,
                  i == 0 ? restoreSource : null,
                  i == 0,
                  ShardRoutingState.UNASSIGNED,
                  0));
        }
        shards.put(shardId, indexShardRoutingBuilder.build());
      }
      return this;
    }

    /** Initializes a new empty index, with an option to control if its from an API or not. */
    private Builder initializeEmpty(IndexMetaData indexMetaData, boolean asNew) {
      if (!shards.isEmpty()) {
        throw new ElasticSearchIllegalStateException(
            "trying to initialize an index with fresh shards, but already has shards created");
      }
      for (int shardId = 0; shardId < indexMetaData.numberOfShards(); shardId++) {
        IndexShardRoutingTable.Builder indexShardRoutingBuilder =
            new IndexShardRoutingTable.Builder(
                new ShardId(indexMetaData.index(), shardId), asNew ? false : true);
        for (int i = 0; i <= indexMetaData.numberOfReplicas(); i++) {
          indexShardRoutingBuilder.addShard(
              new ImmutableShardRouting(
                  index, shardId, null, i == 0, ShardRoutingState.UNASSIGNED, 0));
        }
        shards.put(shardId, indexShardRoutingBuilder.build());
      }
      return this;
    }

    public Builder addReplica() {
      for (IntCursor cursor : shards.keys()) {
        int shardId = cursor.value;
        // version 0, will get updated when reroute will happen
        ImmutableShardRouting shard =
            new ImmutableShardRouting(index, shardId, null, false, ShardRoutingState.UNASSIGNED, 0);
        shards.put(
            shardId,
            new IndexShardRoutingTable.Builder(shards.get(shard.id())).addShard(shard).build());
      }
      return this;
    }

    public Builder removeReplica() {
      for (IntCursor cursor : shards.keys()) {
        int shardId = cursor.value;
        IndexShardRoutingTable indexShard = shards.get(shardId);
        if (indexShard.replicaShards().isEmpty()) {
          // nothing to do here!
          return this;
        }
        // re-add all the current ones
        IndexShardRoutingTable.Builder builder =
            new IndexShardRoutingTable.Builder(
                indexShard.shardId(), indexShard.primaryAllocatedPostApi());
        for (ShardRouting shardRouting : indexShard) {
          builder.addShard(new ImmutableShardRouting(shardRouting));
        }
        // first check if there is one that is not assigned to a node, and remove it
        boolean removed = false;
        for (ShardRouting shardRouting : indexShard) {
          if (!shardRouting.primary() && !shardRouting.assignedToNode()) {
            builder.removeShard(shardRouting);
            removed = true;
            break;
          }
        }
        if (!removed) {
          for (ShardRouting shardRouting : indexShard) {
            if (!shardRouting.primary()) {
              builder.removeShard(shardRouting);
              removed = true;
              break;
            }
          }
        }
        shards.put(shardId, builder.build());
      }
      return this;
    }

    public Builder addIndexShard(IndexShardRoutingTable indexShard) {
      shards.put(indexShard.shardId().id(), indexShard);
      return this;
    }

    /** Clears the post allocation flag for the specified shard */
    public Builder clearPostAllocationFlag(ShardId shardId) {
      assert this.index.equals(shardId.index().name());
      IndexShardRoutingTable indexShard = shards.get(shardId.id());
      shards.put(
          indexShard.shardId().id(),
          new IndexShardRoutingTable(indexShard.shardId(), indexShard.shards(), false));
      return this;
    }

    /**
     * Adds a new shard routing (makes a copy of it), with reference data used from the index shard
     * routing table if it needs to be created.
     */
    public Builder addShard(IndexShardRoutingTable refData, ShardRouting shard) {
      IndexShardRoutingTable indexShard = shards.get(shard.id());
      if (indexShard == null) {
        indexShard =
            new IndexShardRoutingTable.Builder(refData.shardId(), refData.primaryAllocatedPostApi())
                .addShard(new ImmutableShardRouting(shard))
                .build();
      } else {
        indexShard =
            new IndexShardRoutingTable.Builder(indexShard)
                .addShard(new ImmutableShardRouting(shard))
                .build();
      }
      shards.put(indexShard.shardId().id(), indexShard);
      return this;
    }

    public IndexRoutingTable build() throws RoutingValidationException {
      IndexRoutingTable indexRoutingTable = new IndexRoutingTable(index, shards.build());
      indexRoutingTable.validate();
      return indexRoutingTable;
    }
  }
 /**
  * Returns <code>true</code> if all primary shards are in {@link ShardRoutingState#UNASSIGNED}
  * state. Otherwise <code>false</code>.
  */
 public boolean allPrimaryShardsUnassigned() {
   return primaryShardsUnassigned() == shards.size();
 }
 public IndexShardRoutingTable shard(int shardId) {
   return shards.get(shardId);
 }
 @Override
 public UnmodifiableIterator<IndexShardRoutingTable> iterator() {
   return shards.valuesIt();
 }
 @Override
 protected int size(ImmutableOpenIntMap<V> map) {
   return map.size();
 }
 @Override
 protected V get(ImmutableOpenIntMap<V> map, Integer key) {
   return map.get(key);
 }
 @Override
 protected ImmutableOpenIntMap<V> createMap(Map values) {
   return ImmutableOpenIntMap.<V>builder().putAll(values).build();
 }
    public IndexMetaData build() {
      ImmutableOpenMap.Builder<String, AliasMetaData> tmpAliases = aliases;
      Settings tmpSettings = settings;

      // update default mapping on the MappingMetaData
      if (mappings.containsKey(MapperService.DEFAULT_MAPPING)) {
        MappingMetaData defaultMapping = mappings.get(MapperService.DEFAULT_MAPPING);
        for (ObjectCursor<MappingMetaData> cursor : mappings.values()) {
          cursor.value.updateDefaultMapping(defaultMapping);
        }
      }

      Integer maybeNumberOfShards = settings.getAsInt(SETTING_NUMBER_OF_SHARDS, null);
      if (maybeNumberOfShards == null) {
        throw new IllegalArgumentException("must specify numberOfShards for index [" + index + "]");
      }
      int numberOfShards = maybeNumberOfShards;
      if (numberOfShards <= 0) {
        throw new IllegalArgumentException(
            "must specify positive number of shards for index [" + index + "]");
      }

      Integer maybeNumberOfReplicas = settings.getAsInt(SETTING_NUMBER_OF_REPLICAS, null);
      if (maybeNumberOfReplicas == null) {
        throw new IllegalArgumentException(
            "must specify numberOfReplicas for index [" + index + "]");
      }
      int numberOfReplicas = maybeNumberOfReplicas;
      if (numberOfReplicas < 0) {
        throw new IllegalArgumentException(
            "must specify non-negative number of shards for index [" + index + "]");
      }

      // fill missing slots in activeAllocationIds with empty set if needed and make all entries
      // immutable
      ImmutableOpenIntMap.Builder<Set<String>> filledActiveAllocationIds =
          ImmutableOpenIntMap.builder();
      for (int i = 0; i < numberOfShards; i++) {
        if (activeAllocationIds.containsKey(i)) {
          filledActiveAllocationIds.put(
              i, Collections.unmodifiableSet(new HashSet<>(activeAllocationIds.get(i))));
        } else {
          filledActiveAllocationIds.put(i, Collections.emptySet());
        }
      }
      final Map<String, String> requireMap =
          INDEX_ROUTING_REQUIRE_GROUP_SETTING.get(settings).getAsMap();
      final DiscoveryNodeFilters requireFilters;
      if (requireMap.isEmpty()) {
        requireFilters = null;
      } else {
        requireFilters = DiscoveryNodeFilters.buildFromKeyValue(AND, requireMap);
      }
      Map<String, String> includeMap = INDEX_ROUTING_INCLUDE_GROUP_SETTING.get(settings).getAsMap();
      final DiscoveryNodeFilters includeFilters;
      if (includeMap.isEmpty()) {
        includeFilters = null;
      } else {
        includeFilters = DiscoveryNodeFilters.buildFromKeyValue(OR, includeMap);
      }
      Map<String, String> excludeMap = INDEX_ROUTING_EXCLUDE_GROUP_SETTING.get(settings).getAsMap();
      final DiscoveryNodeFilters excludeFilters;
      if (excludeMap.isEmpty()) {
        excludeFilters = null;
      } else {
        excludeFilters = DiscoveryNodeFilters.buildFromKeyValue(OR, excludeMap);
      }
      Version indexCreatedVersion = Version.indexCreated(settings);
      Version indexUpgradedVersion =
          settings.getAsVersion(IndexMetaData.SETTING_VERSION_UPGRADED, indexCreatedVersion);
      String stringLuceneVersion = settings.get(SETTING_VERSION_MINIMUM_COMPATIBLE);
      final org.apache.lucene.util.Version minimumCompatibleLuceneVersion;
      if (stringLuceneVersion != null) {
        try {
          minimumCompatibleLuceneVersion =
              org.apache.lucene.util.Version.parse(stringLuceneVersion);
        } catch (ParseException ex) {
          throw new IllegalStateException(
              "Cannot parse lucene version ["
                  + stringLuceneVersion
                  + "] in the ["
                  + SETTING_VERSION_MINIMUM_COMPATIBLE
                  + "] setting",
              ex);
        }
      } else {
        minimumCompatibleLuceneVersion = null;
      }

      final String uuid = settings.get(SETTING_INDEX_UUID, INDEX_UUID_NA_VALUE);
      return new IndexMetaData(
          new Index(index, uuid),
          version,
          state,
          numberOfShards,
          numberOfReplicas,
          tmpSettings,
          mappings.build(),
          tmpAliases.build(),
          customs.build(),
          filledActiveAllocationIds.build(),
          requireFilters,
          includeFilters,
          excludeFilters,
          indexCreatedVersion,
          indexUpgradedVersion,
          minimumCompatibleLuceneVersion);
    }