@SuppressWarnings("unchecked")
  public <IFD extends IndexFieldData<?>> IFD getForField(MappedFieldType fieldType) {
    final Names fieldNames = fieldType.names();
    final FieldDataType type = fieldType.fieldDataType();
    if (type == null) {
      throw new IllegalArgumentException(
          "found no fielddata type for field [" + fieldNames.fullName() + "]");
    }
    final boolean docValues = fieldType.hasDocValues();
    IndexFieldData.Builder builder = null;
    String format = type.getFormat(indexSettings);
    if (format != null && FieldDataType.DOC_VALUES_FORMAT_VALUE.equals(format) && !docValues) {
      logger.warn(
          "field ["
              + fieldNames.fullName()
              + "] has no doc values, will use default field data format");
      format = null;
    }
    if (format != null) {
      builder = buildersByTypeAndFormat.get(Tuple.tuple(type.getType(), format));
      if (builder == null) {
        logger.warn(
            "failed to find format ["
                + format
                + "] for field ["
                + fieldNames.fullName()
                + "], will use default");
      }
    }
    if (builder == null && docValues) {
      builder = docValuesBuildersByType.get(type.getType());
    }
    if (builder == null) {
      builder = buildersByType.get(type.getType());
    }
    if (builder == null) {
      throw new IllegalArgumentException(
          "failed to find field data builder for field "
              + fieldNames.fullName()
              + ", and type "
              + type.getType());
    }

    IndexFieldDataCache cache;
    synchronized (this) {
      cache = fieldDataCaches.get(fieldNames.indexName());
      if (cache == null) {
        //  we default to node level cache, which in turn defaults to be unbounded
        // this means changing the node level settings is simple, just set the bounds there
        String cacheType =
            type.getSettings()
                .get("cache", indexSettings.get(FIELDDATA_CACHE_KEY, FIELDDATA_CACHE_VALUE_NODE));
        if (FIELDDATA_CACHE_VALUE_NODE.equals(cacheType)) {
          cache = indicesFieldDataCache.buildIndexFieldDataCache(listener, index, fieldNames, type);
        } else if ("none".equals(cacheType)) {
          cache = new IndexFieldDataCache.None();
        } else {
          throw new IllegalArgumentException(
              "cache type not supported ["
                  + cacheType
                  + "] for field ["
                  + fieldNames.fullName()
                  + "]");
        }
        fieldDataCaches.put(fieldNames.indexName(), cache);
      }
    }

    return (IFD)
        builder.build(index, indexSettings, fieldType, cache, circuitBreakerService, mapperService);
  }