h2o-bindings/bin/gen_java.py
def generate_proxy(classname, endpoints):
    """
    Generate a Retrofit Proxy class.

    Retrofit interfaces look like this:
        public interface GitHubService {
            @GET("/users/{user}/repos")
            Call<List<Repo>> listRepos(@Path("user") String user);
        }
      :param classname: name of the class
      :param endpoints: list of endpoints served by this class
    """
    # Replace path vars like (?<schemaname>.*) with {schemaname} for Retrofit's annotation
    var_pattern = re.compile(r"\{(\w+)\}")

    helper_class = []
    found_key_array_parameter = False

    yield "/*"
    yield " * This file is auto-generated by h2o-3/h2o-bindings/bin/gen_java.py"
    yield " * Copyright 2016 H2O.ai;  Apache License Version 2.0 (see LICENSE for details)"
    yield " */"
    yield "package water.bindings.proxies.retrofit;"
    yield ""
    yield "import water.bindings.pojos.*;"
    yield "import retrofit2.*;"
    yield "import retrofit2.http.*;"
    yield "import java.util.Map;" if classname == "Grid" or classname == "ModelBuilders" else None
    yield ""
    yield "public interface " + classname + " {"
    yield ""

    for e in endpoints:
        method = e["handler_method"]
        # should we always use e.api_name ?
        if method == "exec":
            method = e["api_name"]

        param_strs = []
        required_param_strs = []
        for field in e["input_params"]:
            fname = field["name"]
            ftype = "Path" if field["is_path_param"] else "Field"
            ptype = translate_type(field["type"], field["schema_name"])
            if ptype.endswith("KeyV3") or ptype == "ColSpecifierV3": ptype = "String"
            if ptype.endswith("KeyV3[]"): ptype = "String[]"
            param_str = "@{ftype}(\"{fname}\") {ptype} {fname}".format(**locals())
            param_strs.append(param_str)
            if field["required"]:
                required_param_strs.append(param_str)
        if len(param_strs) == len(required_param_strs): required_param_strs = None

        yield u"  /** "
        yield bi.wrap(e["summary"], indent="   * ")
        for field in e["input_params"]:
            s = "   *   @param %s " % field["name"]
            yield s + bi.wrap(field["help"], indent="   *" + " " * (len(s) - 4), indent_first=False)
        yield u"   */"
        # Create 2 versions of each call: first with all input parameters present, and then only with required params
        for params in [param_strs, required_param_strs]:
            if params is None: continue
            yield u"  @FormUrlEncoded" if e["http_method"] == "POST" else None
            yield u"  @{method}(\"{path}\")".format(method=e["http_method"], path=e["url_pattern"])
            if len(params) <= 1:
                args = params[0] if params else ""
                yield "  Call<{schema}> {method}({args});".format(schema=e["output_schema"], method=method, args=args)
            else:
                yield "  Call<{schema}> {method}(".format(schema=e["output_schema"], method=method)
                for arg in params:
                    yield "    " + arg + ("" if arg == params[-1] else ",")
                yield "  );"
            yield ""

        # Make special static Helper class for Grid and ModelBuilders.
        if "algo" in e:
            # We make two train_ and validate_ methods.  One (built here) takes the parameters schema, the other
            # (built above) takes each parameter.
            helper_class.append("    /**")
            helper_class.append(bi.wrap(e["summary"], indent="     * "))
            helper_class.append("     */")
            helper_class.append("    public static Call<{oschema}> {method}({outer_class} z, {ischema} p) {{"
                                .format(ischema=e["input_schema"], oschema=e["output_schema"], method=method,
                                        outer_class=classname))
            helper_class.append("      return z.{method}(".format(method=method))
            for field in e["input_params"]:
                ptype = translate_type(field["type"], field["schema_name"])
                pname = translate_name(field["name"])
                if ptype.endswith("KeyV3"):
                    s = "(p.{parm} == null? null : p.{parm}.name)".format(parm=pname)
                elif ptype.endswith("KeyV3[]"):
                    found_key_array_parameter = True
                    s = "(p.{parm} == null? null : keyArrayToStringArray(p.{parm}))".format(parm=pname)
                elif ptype.startswith("ColSpecifier"):
                    s = "(p.{parm} == null? null : p.{parm}.columnName)".format(parm=pname)
                else:
                    s = "p." + pname
                if field != e["input_params"][-1]:
                    s += ","
                helper_class.append("        " + s)
            helper_class.append("      );")
            helper_class.append("    }")
            helper_class.append("")

    if helper_class:
        yield ""
        yield "  @SuppressWarnings(\"unused\")"
        yield "  class Helper {"
        for line in helper_class:
            yield line
        if found_key_array_parameter:
            yield "    /**"
            yield "     * Return an array of Strings for an array of keys."
            yield "     */"
            yield "    public static String[] keyArrayToStringArray(KeyV3[] keys) {"
            yield "      if (keys == null) return null;"
            yield "      String[] ids = new String[keys.length];"
            yield "      int i = 0;"
            yield "      for (KeyV3 key : keys) ids[i++] = key.name;"
            yield "      return ids;"
            yield "    }"
        yield "  }"
        yield ""

    yield "}"
def generate_proxy(classname, endpoints):
    """
    Generate a Retrofit Proxy class.

    Retrofit interfaces look like this:
        public interface GitHubService {
            @GET("/users/{user}/repos")
            Call<List<Repo>> listRepos(@Path("user") String user);
        }
      :param classname: name of the class
      :param endpoints: list of endpoints served by this class
    """
    # Replace path vars like (?<schemaname>.*) with {schemaname} for Retrofit's annotation
    var_pattern = re.compile(r"\{(\w+)\}")

    helper_class = []
    found_key_array_parameter = False

    yield "/*"
    yield " * This file is auto-generated by h2o-3/h2o-bindings/bin/gen_java.py"
    yield " * Copyright 2016 H2O.ai;  Apache License Version 2.0 (see LICENSE for details)"
    yield " */"
    yield "package water.bindings.proxies.retrofit;"
    yield ""
    yield "import water.bindings.pojos.*;"
    yield "import retrofit2.*;"
    yield "import retrofit2.http.*;"
    yield "import java.util.Map;" if classname == "Grid" or classname == "ModelBuilders" else None
    yield ""
    yield "public interface " + classname + " {"
    yield ""

    for e in endpoints:
        method = e["handler_method"]
        # should we always use e.api_name ?
        if method == "exec":
            method = e["api_name"]

        param_strs = []
        required_param_strs = []
        for field in e["input_params"]:
            fname = field["name"]
            ftype = "Path" if field["is_path_param"] else "Field"
            ptype = translate_type(field["type"], field["schema_name"])
            if ptype.endswith("KeyV3") or ptype == "ColSpecifierV3": ptype = "String"
            if ptype.endswith("KeyV3[]"): ptype = "String[]"
            param_str = "@{ftype}(\"{fname}\") {ptype} {fname}".format(**locals())
            param_strs.append(param_str)
            if field["required"]:
                required_param_strs.append(param_str)
        if len(param_strs) == len(required_param_strs): required_param_strs = None

        yield u"  /** "
        yield bi.wrap(e["summary"], indent="   * ")
        for field in e["input_params"]:
            s = "   *   @param %s " % field["name"]
            yield s + bi.wrap(field["help"], indent="   *" + " " * (len(s) - 4), indent_first=False)
        yield u"   */"
        # Create 2 versions of each call: first with all input parameters present, and then only with required params
        for params in [param_strs, required_param_strs]:
            if params is None: continue
            yield u"  @FormUrlEncoded" if e["http_method"] == "POST" else None
            yield u"  @{method}(\"{path}\")".format(method=e["http_method"], path=e["url_pattern"])
            if len(params) <= 1:
                args = params[0] if params else ""
                yield "  Call<{schema}> {method}({args});".format(schema=e["output_schema"], method=method, args=args)
            else:
                yield "  Call<{schema}> {method}(".format(schema=e["output_schema"], method=method)
                for arg in params:
                    yield "    " + arg + ("" if arg == params[-1] else ",")
                yield "  );"
            yield ""

        # Make special static Helper class for Grid and ModelBuilders.
        if "algo" in e:
            # We make two train_ and validate_ methods.  One (built here) takes the parameters schema, the other
            # (built above) takes each parameter.
            helper_class.append("    /**")
            helper_class.append(bi.wrap(e["summary"], indent="     * "))
            helper_class.append("     */")
            helper_class.append("    public static Call<{oschema}> {method}({outer_class} z, {ischema} p) {{"
                                .format(ischema=e["input_schema"], oschema=e["output_schema"], method=method,
                                        outer_class=classname))
            helper_class.append("      return z.{method}(".format(method=method))
            for field in e["input_params"]:
                ptype = translate_type(field["type"], field["schema_name"])
                pname = translate_name(field["name"])
                if ptype.endswith("KeyV3"):
                    s = "(p.{parm} == null? null : p.{parm}.name)".format(parm=pname)
                elif ptype.endswith("KeyV3[]"):
                    found_key_array_parameter = True
                    s = "(p.{parm} == null? null : keyArrayToStringArray(p.{parm}))".format(parm=pname)
                elif ptype.startswith("ColSpecifier"):
                    s = "(p.{parm} == null? null : p.{parm}.columnName)".format(parm=pname)
                else:
                    s = "p." + pname
                if field != e["input_params"][-1]:
                    s += ","
                helper_class.append("        " + s)
            helper_class.append("      );")
            helper_class.append("    }")
            helper_class.append("")

    if helper_class:
        yield ""
        yield "  @SuppressWarnings(\"unused\")"
        yield "  class Helper {"
        for line in helper_class:
            yield line
        if found_key_array_parameter:
            yield "    /**"
            yield "     * Return an array of Strings for an array of keys."
            yield "     */"
            yield "    public static String[] keyArrayToStringArray(KeyV3[] keys) {"
            yield "      if (keys == null) return null;"
            yield "      String[] ids = new String[keys.length];"
            yield "      int i = 0;"
            yield "      for (KeyV3 key : keys) ids[i++] = key.name;"
            yield "      return ids;"
            yield "    }"
        yield "  }"
        yield ""

    yield "}"
Generate a Retrofit Proxy class.

    Retrofit interfaces look like this:
        public interface GitHubService {
            @GET("/users/{user}/repos")
            Call<List<Repo>> listRepos(@Path("user") String user);
        }
      :param classname: name of the class
      :param endpoints: list of endpoints served by this class