content: /*
   * Copyright (c) 2024, Alibaba Cloud;
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.aliyun.dataworks.migrationx.transformer.core.sqoop;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FileReader;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Provides utility functions to read in options file. An options file is a regular text file with each line specifying
   * a separate option. An option may continue into a following line by using a back-slash separator character at the end
   * of the non-terminating line. Options file also allow empty lines and comment lines which are disregarded. Comment
   * lines must begin with the hash character as the first character. Leading and trailing white-spaces are ignored for
   * any options read from the Options file.
   */
  public final class OptionsFileUtil {
  
      public static final Logger LOG = LoggerFactory.getLogger(
          OptionsFileUtil.class.getName());
  
      private OptionsFileUtil() {
      }
  
      /**
       * Expands any options file that may be present in the given set of arguments.
       *
       * @param args the given arguments
       * @return a new string array that contains the expanded arguments.
       * @throws Exception
       */
      public static String[] expandArguments(String[] args) throws Exception {
          List<String> options = new ArrayList<String>();
  
          for (int i = 0; i < args.length; i++) {
              if (args[i].equals(Sqoop.SQOOP_OPTIONS_FILE_SPECIFIER)) {
                  if (i == args.length - 1) {
                      throw new Exception("Missing options file");
                  }
  
                  String fileName = args[++i];
                  File optionsFile = new File(fileName);
                  BufferedReader reader = null;
                  StringBuilder buffer = new StringBuilder();
                  try {
                      reader = new BufferedReader(new FileReader(optionsFile));
                      String nextLine = null;
                      while ((nextLine = reader.readLine()) != null) {
                          nextLine = nextLine.trim();
                          if (nextLine.length() == 0 || nextLine.startsWith("#")) {
                              // empty line or comment
                              continue;
                          }
  
                          buffer.append(nextLine);
                          if (nextLine.endsWith("\\")) {
                              if (buffer.charAt(0) == '\'' || buffer.charAt(0) == '"') {
                                  throw new Exception(
                                      "Multiline quoted strings not supported in file("
                                          + fileName + "): " + buffer.toString());
                              }
                              // Remove the trailing back-slash and continue
                              buffer.deleteCharAt(buffer.length() - 1);
                          } else {
                              // The buffer contains a full option
                              options.add(
                                  removeQuotesEncolosingOption(fileName, buffer.toString()));
                              buffer.delete(0, buffer.length());
                          }
                      }
  
                      // Assert that the buffer is empty
                      if (buffer.length() != 0) {
                          throw new Exception("Malformed option in options file("
                              + fileName + "): " + buffer.toString());
                      }
                  } catch (IOException ex) {
                      throw new Exception("Unable to read options file: " + fileName, ex);
                  } finally {
                      if (reader != null) {
                          try {
                              reader.close();
                          } catch (IOException ex) {
                              LOG.info("Exception while closing reader", ex);
                          }
                      }
                  }
              } else {
                  // Regular option. Parse it and put it on the appropriate list
                  options.add(args[i]);
              }
          }
  
          return options.toArray(new String[options.size()]);
      }
  
      /**
       * Removes the surrounding quote characters as needed. It first attempts to remove surrounding double quotes. If
       * successful, the resultant string is returned. If no surrounding double quotes are found, it attempts to remove
       * surrounding single quote characters. If successful, the resultant string is returned. If not the original string
       * is returnred.
       *
       * @param fileName
       * @param option
       * @return
       * @throws Exception
       */
      private static String removeQuotesEncolosingOption(
          String fileName, String option) throws Exception {
  
          // Attempt to remove double quotes. If successful, return.
          String option1 = removeQuoteCharactersIfNecessary(fileName, option, '"');
          if (!option1.equals(option)) {
              // Quotes were successfully removed
              return option1;
          }
  
          // Attempt to remove single quotes.
          return removeQuoteCharactersIfNecessary(fileName, option, '\'');
      }
  
      /**
       * Removes the surrounding quote characters from the given string. The quotes are identified by the quote parameter,
       * the given string by option. The fileName parameter is used for raising exceptions with relevant message.
       *
       * @param fileName
       * @param option
       * @param quote
       * @return
       * @throws Exception
       */
      private static String removeQuoteCharactersIfNecessary(String fileName,
                                                             String option, char quote) throws Exception {
          boolean startingQuote = (option.charAt(0) == quote);
          boolean endingQuote = (option.charAt(option.length() - 1) == quote);
  
          if (startingQuote && endingQuote) {
              if (option.length() == 1) {
                  throw new Exception("Malformed option in options file("
                      + fileName + "): " + option);
              }
              return option.substring(1, option.length() - 1);
          }
  
          if (startingQuote || endingQuote) {
              throw new Exception("Malformed option in options file("
                  + fileName + "): " + option);
          }
  
          return option;
      }
  
  }
  