docs: void parseArgs(String[] args) {
      int i = 0;
      while (true) {
        if (i >= args.length) {
          break;
        }
  
        String s = args[i];
        if (s.equals("-h") ||
                s.equals("help") ||
                s.equals("-help") ||
                s.equals("--help")) {
          usage();
        }
        else if (s.equals("-n") ||
                s.equals("-nodes")) {
          i++; if (i >= args.length) { usage(); }
          numNodes = Integer.parseInt(args[i]);
        }
        else if (s.equals("-o") ||
                s.equals("-output")) {
          i++; if (i >= args.length) { usage(); }
          outputPath = args[i];
        }
        else if (s.equals("-jobname")) {
          i++; if (i >= args.length) { usage(); }
          jobtrackerName = args[i];
        }
        else if (s.equals("-mapperXmx")) {
          i++; if (i >= args.length) { usage(); }
          mapperXmx = args[i];
        }
        else if (s.equals("-extramempercent")) {
          i++; if (i >= args.length) { usage(); }
          extraMemPercent = Integer.parseInt(args[i]);
        }
        else if (s.equals("-driverif")) {
          i++; if (i >= args.length) { usage(); }
          driverCallbackIp = args[i];
        }
        else if (s.equals("-driverport")) {
          i++; if (i >= args.length) { usage(); }
          driverCallbackPort = Integer.parseInt(args[i]);
        }
        else if (s.equals("-network")) {
          i++; if (i >= args.length) { usage(); }
          network = args[i];
        }
        else if (s.equals("-timeout")) {
          i++; if (i >= args.length) { usage(); }
          cloudFormationTimeoutSeconds = Integer.parseInt(args[i]);
        }
        else if (s.equals("-disown")) {
          disown = true;
        }
        else if (s.equals("-notify")) {
          i++; if (i >= args.length) { usage(); }
          clusterReadyFileName = args[i];
        }
        else if (s.equals("-nthreads")) {
          i++; if (i >= args.length) { usage(); }
          nthreads = Integer.parseInt(args[i]);
        }
        else if (s.equals("-many_cols")) {
          manyCols = true;
        }
        else if (s.equals("-chunk_bytes")) {
          i++; if (i >= args.length) { usage(); }
          chunk_bytes = Integer.parseInt(args[i]);
        }
        else if (s.equals("-data_max_factor_levels")) {
          i++; if (i >= args.length) { usage(); }
          data_max_factor_levels = Integer.parseInt(args[i]);
        }
        else if (s.equals("-baseport")) {
          i++; if (i >= args.length) { usage(); }
          basePort = Integer.parseInt(args[i]);
          if ((basePort < 0) || (basePort > 65535)) {
              error("Base port must be between 1 and 65535");
          }
        }
        else if (s.equals("-beta")) {
          beta = true;
        }
        else if (s.equals("-random_udp_drop")) {
          enableRandomUdpDrop = true;
        }
        else if (s.equals("-ea")) {
          enableExceptions = true;
        }
        else if (s.equals("-verbose:gc")) {
          enableVerboseGC = true;
        }
        else if (s.equals("-verbose:class")) {
          enableVerboseClass = true;
        }
        else if (s.equals("-XX:+PrintCompilation")) {
          enablePrintCompilation = true;
        }
        else if (s.equals("-exclude")) {
          enableExcludeMethods = true;
        }
        else if (s.equals("-Dlog4j.defaultInitOverride=true")) {
          enableLog4jDefaultInitOverride = true;
        }
        else if (s.equals("-debug")) {
          enableDebug = true;
        }
        else if (s.equals("-suspend")) {
          enableSuspend = true;
        }
        else if (s.equals("-debugport")) {
          i++; if (i >= args.length) { usage(); }
          debugPort = Integer.parseInt(args[i]);
          if ((debugPort < 0) || (debugPort > 65535)) {
            error("Debug port must be between 1 and 65535");
          }
        }
        else if (s.equals("-XX:+PrintGCDetails")) {
          enablePrintGCDetails = true;
        }
        else if (s.equals("-XX:+PrintGCTimeStamps")) {
          enablePrintGCTimeStamps = true;
        }
        else if (s.equals("-gc")) {
          enableVerboseGC = true;
          enablePrintGCDetails = true;
          enablePrintGCTimeStamps = true;
        }
        else if (s.equals("-license")) {
          i++; if (i >= args.length) { usage(); }
          licenseFileName = args[i];
        }
        else if (s.equals("-extraJavaOpts")) {
          i++; if (i >= args.length) { usage(); }
          extraJavaOpts = args[i];
        }
        else {
          error("Unrecognized option " + s);
        }
  
        i++;
      }
  
      // Check for mandatory arguments.
      if (numNodes < 1) {
        error("Number of H2O nodes must be greater than 0 (must specify -n)");
      }
      if (outputPath == null) {
        error("Missing required option -output");
      }
      if (mapperXmx == null) {
        error("Missing required option -mapperXmx");
      }
  
      // Check for sane arguments.
      if (! mapperXmx.matches("[1-9][0-9]*[mgMG]")) {
        error("-mapperXmx invalid (try something like -mapperXmx 4g)");
      }
  
      if (extraMemPercent < 0) {
        extraMemPercent = DEFAULT_EXTRA_MEM_PERCENT;
      }
  
      if (jobtrackerName == null) {
        Random rng = new Random();
        int num = rng.nextInt(99999);
        jobtrackerName = "H2O_" + num;
      }
  
      if (network == null) {
        network = "";
      }
      else {
        String[] networks;
        if (network.contains(",")) {
          networks = network.split(",");
        }
        else {
          networks = new String[1];
          networks[0] = network;
        }
  
        for (int j = 0; j < networks.length; j++) {
          String n = networks[j];
          Pattern p = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)/(\\d+)");
          Matcher m = p.matcher(n);
          boolean b = m.matches();
          if (! b) {
            error("network invalid: " + n);
          }
  
          for (int k = 1; k <=4; k++) {
            int o = Integer.parseInt(m.group(k));
            if ((o < 0) || (o > 255)) {
              error("network invalid: " + n);
            }
  
            int bits = Integer.parseInt(m.group(5));
            if ((bits < 0) || (bits > 32)) {
              error("network invalid: " + n);
            }
          }
        }
      }
  
      if (network == null) {
        error("Internal error, network should not be null at this point");
      }
  
      if ((nthreads >= 0) && (nthreads < 4)) {
        error("nthreads invalid (must be >= 4): " + nthreads);
      }
  
      if (licenseFileName != null) {
        try {
          licenseData = readFile(licenseFileName);
        }
        catch (Exception xe) {
          StringBuilder sb = new StringBuilder();
          sb.append("Failed to read license file: ");
          if (xe.getLocalizedMessage() != null) {
            sb.append(xe.getLocalizedMessage());
          }
          else {
            sb.append(licenseFileName);
          }
  
          error(sb.toString());
        }
      }
    }