private static <
          T extends Scan<T, FileScanTask, CombinedScanTask>> Scan<T,
          FileScanTask,
          CombinedScanTask> applyConfig(
          Configuration conf, Scan<T, FileScanTask, CombinedScanTask> scanToConfigure) {

    Scan<T, FileScanTask, CombinedScanTask> scan = scanToConfigure.caseSensitive(
            conf.getBoolean(InputFormatConfig.CASE_SENSITIVE, InputFormatConfig.CASE_SENSITIVE_DEFAULT));

    long splitSize = conf.getLong(InputFormatConfig.SPLIT_SIZE, 0);
    if (splitSize > 0) {
      scan = scan.option(TableProperties.SPLIT_SIZE, String.valueOf(splitSize));
    }

    // In case of LLAP-based execution we ask Iceberg not to combine multiple fileScanTasks into one split.
    // This is so that cache affinity can work, and each file(split) is executed/cached on always the same LLAP daemon.
    MapWork mapWork = LlapHiveUtils.findMapWork((JobConf) conf);
    if (mapWork != null && mapWork.getCacheAffinity()) {
      // Iceberg splits logically consist of buckets, where the bucket size equals to openFileCost setting if the files
      // assigned to such bucket are smaller. This is how Iceberg would combine multiple files into one split, so here
      // we need to enforce the bucket size to be equal to split size to avoid file combination.
      Long openFileCost = splitSize > 0 ? splitSize : TableProperties.SPLIT_SIZE_DEFAULT;
      scan = scan.option(TableProperties.SPLIT_OPEN_FILE_COST, String.valueOf(openFileCost));
    }
    //  TODO: Currently, this projection optimization stored on scan is not being used effectively on Hive side, as
    //   Hive actually uses conf to propagate the projected columns to let the final reader to read the only
    //   projected columns data. See IcebergInputFormat::readSchema(Configuration conf, Table table, boolean
    //   caseSensitive). But we can consider using this projection optimization stored on scan in the future when
    //   needed.
    Schema readSchema = InputFormatConfig.readSchema(conf);
    if (readSchema != null) {
      scan = scan.project(readSchema);
    } else {
      String[] selectedColumns = InputFormatConfig.selectedColumns(conf);
      if (selectedColumns != null) {
        scan = scan.select(selectedColumns);
      }
    }

    // TODO add a filter parser to get rid of Serialization
    Expression filter = SerializationUtil.deserializeFromBase64(conf.get(InputFormatConfig.FILTER_EXPRESSION));
    if (filter != null) {
      // In order to prevent the filter expression to be attached to every file scan task generated we call
      // ignoreResiduals() here. The passed in filter will still be effective during split generation.
      // On the execution side residual expressions will be mined from the passed job conf.
      scan = scan.filter(filter).ignoreResiduals();
    }
    return scan;
  }
Configures the {@code Job} to use the {@code IcebergInputFormat} and
returns a helper to add further configuration.

@param job the {@code Job} to configure
iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/mapreduce/IcebergInputFormat.java