code: package io.ray.runtime.util.generator;
  
  import java.io.File;
  import java.io.IOException;
  import java.nio.charset.Charset;
  import java.util.ArrayList;
  import java.util.List;
  import org.apache.commons.io.FileUtils;
  
  public class ConcurrencyGroupBuilderGenerator extends BaseGenerator {
  
    /**
     * Build `BaseConcurrencyGroupBuilder::addMethod()` methods with the given number of parameters.
     *
     * @param numParameters the number of parameters.
     * @param hasReturn if true, Build api for functions with return.
     */
    private void buildConcurrencyGroupMethods(int numParameters, boolean hasReturn) {
      // 1) Construct the `genericTypes` part, e.g. `<T0, T1, T2, R>`.
      StringBuilder genericTypes = new StringBuilder();
      for (int i = 0; i < numParameters; i++) {
        genericTypes.append("T").append(i).append(", ");
      }
      // Return generic type.
      if (hasReturn) {
        genericTypes.append("R, ");
      }
      if (genericTypes.length() > 0) {
        // Trim trailing ", ";
        genericTypes = new StringBuilder(genericTypes.substring(0, genericTypes.length() - 2));
        genericTypes = new StringBuilder("<" + genericTypes + ">");
      }
      // 2) Construct the `returnType` part.
      final String returnType =
          hasReturn ? "ConcurrencyGroupBuilder<A>" : "ConcurrencyGroupBuilder<A>";
      // 3) Construct the `argsDeclaration` part.
      String rayFuncGenericTypes = genericTypes.toString();
      if (rayFuncGenericTypes.isEmpty()) {
        rayFuncGenericTypes = "<A>";
      } else {
        rayFuncGenericTypes = rayFuncGenericTypes.replace("<", "<A, ");
      }
      String argsDeclarationPrefix =
          String.format(
              "RayFunc%s%d%s f, ", hasReturn ? "" : "Void", numParameters + 1, rayFuncGenericTypes);
      // Enumerate all combinations of the parameters.
      for (String param : generateParameters(numParameters)) {
        String argsDeclaration = argsDeclarationPrefix + param;
        // Trim trailing ", ";
        argsDeclaration = argsDeclaration.substring(0, argsDeclaration.length() - 2);
        // Print the first line (method signature).
        final String modifiers = "public";
        final String callFunc = "addMethod";
        newLine(
            1,
            String.format(
                "%s%s %s %s(%s) {",
                modifiers,
                (genericTypes.length() == 0) ? "" : " " + genericTypes,
                returnType,
                callFunc,
                argsDeclaration));
        // 5) Construct the third line.
        newLine(2, "return internalAddMethod(f);");
        newLine(1, "}");
        newLine("");
      }
    }
  
    /** Returns Whole file content of `BaseConcurrencyGroupBuilderGenerator.java`. */
    private String generateActorCallDotJava() {
      sb = new StringBuilder();
      newLine("// Generated by `BaseConcurrencyGroupBuilderGenerator.java`. DO NOT EDIT.");
      newLine("");
      newLine("package io.ray.api.concurrencygroup;");
      newLine("");
      newLine("import io.ray.api.function.RayFunc;");
      for (int i = 1; i <= MAX_PARAMETERS; i++) {
        newLine("import io.ray.api.function.RayFunc" + i + ";");
      }
      for (int i = 1; i <= MAX_PARAMETERS; i++) {
        newLine("import io.ray.api.function.RayFuncVoid" + i + ";");
      }
      newLine("");
      newLine("/**");
      newLine(" * This class provides type-safe interfaces for concurrency groups.");
      newLine(" **/");
      newLine("public abstract class BaseConcurrencyGroupBuilder<A> {");
      newLine("");
      newLine(
          "  protected abstract " + "ConcurrencyGroupBuilder<A> internalAddMethod(RayFunc func);");
      newLine("");
      for (int i = 0; i <= MAX_PARAMETERS - 1; i++) {
        buildConcurrencyGroupMethods(i, true);
        buildConcurrencyGroupMethods(i, false);
      }
      newLine("}");
      return sb.toString();
    }
  
    private List<String> generateParameters(int numParams) {
      List<String> res = new ArrayList<>();
      dfs(0, numParams, "", res);
      return res;
    }
  
    private void dfs(int pos, int numParams, String cur, List<String> res) {
      if (pos >= numParams) {
        res.add(cur);
        return;
      }
      String nextParameter = "";
      dfs(pos + 1, numParams, cur + nextParameter, res);
    }
  
    public static void main(String[] args) throws IOException {
      String path =
          System.getProperty("user.dir")
              + "/api/src/main/java/io/ray/api/"
              + "concurrencyGroup/BaseConcurrencyGroupBuilder.java";
      FileUtils.write(
          new File(path),
          new ConcurrencyGroupBuilderGenerator().generateActorCallDotJava(),
          Charset.defaultCharset());
    }
  }
  