/** Factory method to build up an {@code ExecutionCommand} based on supplied parameters. */
    public static ExecutionCommand parseArgs(String[] args) {
      Option tableOption =
          new Option(
              "t",
              "table",
              true,
              "Overrides the table into which the CSV data is loaded and is case sensitive");
      Option headerOption =
          new Option(
              "h",
              "header",
              true,
              "Overrides the column names to"
                  + " which the CSV data maps and is case sensitive. A special value of "
                  + "in-line indicating that the first line of the CSV file determines the "
                  + "column to which the data maps");
      Option strictOption =
          new Option(
              "s",
              "strict",
              false,
              "Use strict mode by throwing "
                  + "an exception if a column name doesn't match during CSV loading");
      Option delimiterOption =
          new Option(
              "d",
              "delimiter",
              true,
              "Field delimiter for CSV loader. A digit is interpreted as "
                  + "1 -> ctrl A, 2 -> ctrl B ... 9 -> ctrl I.");
      Option quoteCharacterOption =
          new Option(
              "q",
              "quote-character",
              true,
              "Quote character for CSV loader. A digit is interpreted as a control " + "character");
      Option escapeCharacterOption =
          new Option(
              "e",
              "escape-character",
              true,
              "Escape character for CSV loader. A digit is interpreted as a control "
                  + "character");
      Option arrayValueSeparatorOption =
          new Option(
              "a", "array-separator", true, "Define the array element separator, defaults to ':'");
      Options options = new Options();
      options.addOption(tableOption);
      options.addOption(headerOption);
      options.addOption(strictOption);
      options.addOption(delimiterOption);
      options.addOption(quoteCharacterOption);
      options.addOption(escapeCharacterOption);
      options.addOption(arrayValueSeparatorOption);

      CommandLineParser parser = new PosixParser();
      CommandLine cmdLine = null;
      try {
        cmdLine = parser.parse(options, args);
      } catch (ParseException e) {
        usageError(options);
      }

      ExecutionCommand execCmd = new ExecutionCommand();

      if (cmdLine.hasOption(tableOption.getOpt())) {
        execCmd.tableName = cmdLine.getOptionValue(tableOption.getOpt());
      }

      if (cmdLine.hasOption(headerOption.getOpt())) {
        String columnString = cmdLine.getOptionValue(headerOption.getOpt());
        if (HEADER_IN_LINE.equals(columnString)) {
          execCmd.columns = ImmutableList.of();
        } else {
          execCmd.columns =
              ImmutableList.copyOf(Splitter.on(",").trimResults().split(columnString));
        }
      }

      execCmd.strict = cmdLine.hasOption(strictOption.getOpt());
      execCmd.fieldDelimiter = getCharacter(cmdLine.getOptionValue(delimiterOption.getOpt(), ","));
      execCmd.quoteCharacter =
          getCharacter(cmdLine.getOptionValue(quoteCharacterOption.getOpt(), "\""));

      if (cmdLine.hasOption(escapeCharacterOption.getOpt())) {
        execCmd.escapeCharacter =
            getCharacter(cmdLine.getOptionValue(escapeCharacterOption.getOpt(), "\\"));
      }

      execCmd.arrayElementSeparator =
          cmdLine.getOptionValue(
              arrayValueSeparatorOption.getOpt(), CSVCommonsLoader.DEFAULT_ARRAY_ELEMENT_SEPARATOR);

      List<String> argList = Lists.newArrayList(cmdLine.getArgList());
      if (argList.isEmpty()) {
        usageError("Connection string to HBase must be supplied", options);
      }
      execCmd.connectionString = argList.remove(0);
      List<String> inputFiles = Lists.newArrayList();
      for (String arg : argList) {
        if (arg.endsWith(CSV_FILE_EXT) || arg.endsWith(SQL_FILE_EXT)) {
          inputFiles.add(arg);
        } else {
          usageError("Don't know how to interpret argument '" + arg + "'", options);
        }
      }

      if (inputFiles.isEmpty()) {
        usageError("At least one input file must be supplied", options);
      }

      execCmd.inputFiles = inputFiles;

      return execCmd;
    }
  /**
   * Provides a mechanism to run SQL scripts against, where the arguments are: 1) connection URL
   * string 2) one or more paths to either SQL scripts or CSV files If a CurrentSCN property is set
   * on the connection URL, then it is incremented between processing, with each file being
   * processed by a new connection at the increment timestamp value.
   */
  public static void main(String[] args) {

    ExecutionCommand execCmd = ExecutionCommand.parseArgs(args);
    String jdbcUrl = JDBC_PROTOCOL + JDBC_PROTOCOL_SEPARATOR + execCmd.getConnectionString();

    PhoenixConnection conn = null;
    try {
      Properties props = new Properties();
      conn = DriverManager.getConnection(jdbcUrl, props).unwrap(PhoenixConnection.class);

      for (String inputFile : execCmd.getInputFiles()) {
        if (inputFile.endsWith(SQL_FILE_EXT)) {
          PhoenixRuntime.executeStatements(
              conn, new FileReader(inputFile), Collections.emptyList());
        } else if (inputFile.endsWith(CSV_FILE_EXT)) {

          String tableName = execCmd.getTableName();
          if (tableName == null) {
            tableName =
                SchemaUtil.normalizeIdentifier(
                    inputFile.substring(
                        inputFile.lastIndexOf(File.separatorChar) + 1,
                        inputFile.length() - CSV_FILE_EXT.length()));
          }
          CSVCommonsLoader csvLoader =
              new CSVCommonsLoader(
                  conn,
                  tableName,
                  execCmd.getColumns(),
                  execCmd.isStrict(),
                  execCmd.getFieldDelimiter(),
                  execCmd.getQuoteCharacter(),
                  execCmd.getEscapeCharacter(),
                  execCmd.getArrayElementSeparator());
          csvLoader.upsert(inputFile);
        }
      }
    } catch (Throwable t) {
      t.printStackTrace();
    } finally {
      if (conn != null) {
        try {
          conn.close();
        } catch (SQLException e) {
          // going to shut jvm down anyway. So might as well feast on it.
        }
      }
      System.exit(0);
    }
  }