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.apache.commons.cli.GnuParser;
  import org.apache.commons.cli.MissingArgumentException;
  import org.apache.commons.cli.Option;
  import org.apache.commons.cli.ParseException;
  import org.apache.hadoop.util.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.ListIterator;
  
  /**
   * Options parser that follows Sqoop's specific options-parsing needs.
   * <ul>
   *   <li>Option values may contain the '&quot;' character as the first
   *   or last character in the value.</li>
   *   <li>The argument '--' must be preserved in the returned (unparsed)
   *   argument list.</li>
   * </ul>
   */
  public class SqoopParser extends GnuParser {
  
      public static final Logger LOG = LoggerFactory.getLogger(SqoopParser.class.getName());
  
      // We need a handle to Option.addValueForProcessing(String).
      // Since Hadoop will load this in a different classloader than
      // this Sqoop class, we cannot see their package-specific methods.
      // So we just call it by reflection. As long as we're at it, this
      // allows us to also put SqoopParser in its own package.
      private static java.lang.reflect.Method addValForProcessing;
  
      static {
          try {
              addValForProcessing = Option.class.getDeclaredMethod(
                  "addValueForProcessing", String.class);
              addValForProcessing.setAccessible(true);
          } catch (NoSuchMethodException nsme) {
              LOG.error("Could not load required method of Parser: "
                  + StringUtils.stringifyException(nsme));
              addValForProcessing = null;
          }
      }
  
      @Override
      /**
       * Processes arguments to options but only strips matched quotes.
       */
      public void processArgs(Option opt, ListIterator iter)
          throws ParseException {
          // Loop until an option is found.
          while (iter.hasNext()) {
              String str = (String)iter.next();
  
              if (getOptions().hasOption(str) && str.startsWith("-")) {
                  // found an Option, not an argument.
                  iter.previous();
                  break;
              }
  
              // Otherwise, this is a value.
              try {
                  // Note that we only strip matched quotes here.
                  addValForProcessing.invoke(opt, stripMatchedQuotes(str));
              } catch (IllegalAccessException iae) {
                  throw new RuntimeException(iae);
              } catch (java.lang.reflect.InvocationTargetException ite) {
                  // Any runtime exception thrown within addValForProcessing()
                  // will be wrapped in an InvocationTargetException.
                  iter.previous();
                  break;
              } catch (RuntimeException re) {
                  iter.previous();
                  break;
              }
          }
  
          if (opt.getValues() == null && !opt.hasOptionalArg()) {
              throw new MissingArgumentException(opt);
          }
      }
  
      /**
       * Util.stripLeadingAndTrailingQuotes() will strip a '&quot;' character from either or both sides of a string. We
       * only strip the matched pair.
       */
      private String stripMatchedQuotes(String in) {
          if (null == in || "\"".equals(in)) {
              return in; // single quote stays as-is.
          } else if (in.startsWith("\"") && in.endsWith("\"")) {
              // Strip this pair of matched quotes.
              return in.substring(1, in.length() - 1);
          } else {
              // return as-is.
              return in;
          }
      }
  }
  