content: package androidx.test.internal.runner;
  
  import android.text.TextUtils;
  import androidx.test.internal.runner.RunnerArgs.TestArg;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Parses the value of the '-e class' argument.
   *
   * <p>The expected input format is
   * classA#methodB,class2,class3#parameterizedMethod[paramdescription]
   */
  class ClassesArgTokenizer {
  
    private static final String TAG = "ClassesArgTokenizer";
  
    private ClassesArgTokenizer() {}
  
    // Use the state design pattern to parse the input string.
    // The tricky part to handle is the fact that method filters can contain names of Parameterized
    // tests, that can contain arbitrary characters including commas and hashes.
    private abstract static class TokenizerState {
  
      protected final List<TestArg> testArgs;
      protected final String input;
      protected final int startTokenPos;
      protected int pos;
  
      protected TokenizerState(List<TestArg> testArgs, String input, int pos) {
        this.testArgs = testArgs;
        this.input = input;
        this.pos = pos;
        this.startTokenPos = pos;
      }
  
      abstract TokenizerState parse();
    }
  
    private static class ClassTokenizerState extends TokenizerState {
  
      private ClassTokenizerState(List<TestArg> testArgs, String input, int pos) {
        super(testArgs, input, pos);
      }
  
      @Override
      TokenizerState parse() {
        while (pos < input.length()) {
          if (input.charAt(pos) == '#') {
            String className = input.substring(startTokenPos, pos);
            return new MethodTokenizerState(testArgs, input, pos + 1, className).parse();
          }
          if (input.charAt(pos) == ',') {
            String className = input.substring(startTokenPos, pos);
            testArgs.add(new TestArg(className));
            return new ClassTokenizerState(testArgs, input, pos + 1);
          }
          pos++;
        }
        if (pos > startTokenPos) {
          String className = input.substring(startTokenPos, pos);
          testArgs.add(new TestArg(className));
        }
        // end of input
        return null;
      }
    }
  
    private static class MethodTokenizerState extends TokenizerState {
  
      private final String className;
  
      protected MethodTokenizerState(
          List<TestArg> testArgs, String input, int pos, String className) {
        super(testArgs, input, pos);
        this.className = className;
      }
  
      @Override
      TokenizerState parse() {
        while (pos < input.length()) {
          if (input.charAt(pos) == ',') {
            String methodName = input.substring(startTokenPos, pos);
            testArgs.add(new TestArg(className, methodName));
            return new ClassTokenizerState(testArgs, input, pos + 1).parse();
          }
          if (input.charAt(pos) == '[') {
            // Start of parameterized description, ignore all tokens until closed.
            pos = input.indexOf(']', pos);
            if (pos <= 0) {
              throw new IllegalStateException("Could not find closing param ] in input " + input);
            }
          }
          // some special parameterized runners also can have parentheses
          if (input.charAt(pos) == '(') {
            // Start of parameterized description, ignore all tokens until closed.
            pos = input.indexOf(')', pos);
            if (pos <= 0) {
              throw new IllegalStateException("Could not find closing param ) in input " + input);
            }
          }
          pos++;
        }
        if (pos > startTokenPos) {
          String methodName = input.substring(startTokenPos, pos);
          testArgs.add(new TestArg(className, methodName));
        }
        // end of input
        return null;
      }
    }
  
    static List<TestArg> parse(String input) {
      List<TestArg> testargs = new ArrayList<>();
      if (!TextUtils.isEmpty(input)) {
        TokenizerState state = new ClassTokenizerState(testargs, input, 0);
        while (state != null) {
          state = state.parse();
        }
      }
      return testargs;
    }
  }
  