/**
   * Returns the appropriate visitor that type checks the compilation unit according to the type
   * system rules.
   *
   * <p>This implementation uses the checker naming convention to create the appropriate visitor. If
   * no visitor is found, it returns an instance of {@link BaseTypeVisitor}. It reflectively invokes
   * the constructor that accepts this checker and the compilation unit tree (in that order) as
   * arguments.
   *
   * <p>Subclasses have to override this method to create the appropriate visitor if they do not
   * follow the checker naming convention.
   *
   * @param root the compilation unit currently being visited
   * @return the type-checking visitor
   */
  @Override
  protected SourceVisitor<?, ?> createSourceVisitor(CompilationUnitTree root) {

    // Try to reflectively load the visitor.
    Class<?> checkerClass = this.getClass();

    while (checkerClass != BaseTypeChecker.class) {
      final String classToLoad =
          checkerClass.getName().replace("Checker", "Visitor").replace("Subchecker", "Visitor");
      BaseTypeVisitor<?> result =
          invokeConstructorFor(
              classToLoad,
              new Class<?>[] {checkerClass, CompilationUnitTree.class},
              new Object[] {this, root});
      if (result != null) return result;
      checkerClass = checkerClass.getSuperclass();
    }

    // If a visitor couldn't be loaded reflectively, return the default.
    return new BaseTypeVisitor<BaseTypeChecker>(this, root);
  }
  /**
   * Constructs an instance of the appropriate type factory for the implemented type system.
   *
   * <p>The default implementation uses the checker naming convention to create the appropriate type
   * factory. If no factory is found, it returns {@link BasicAnnotatedTypeFactory}. It reflectively
   * invokes the constructor that accepts this checker and compilation unit tree (in that order) as
   * arguments.
   *
   * <p>Subclasses have to override this method to create the appropriate visitor if they do not
   * follow the checker naming convention.
   *
   * @param root the currently visited compilation unit
   * @return the appropriate type factory
   */
  @Override
  public AnnotatedTypeFactory createFactory(CompilationUnitTree root) {

    // Try to reflectively load the type factory.
    Class<?> checkerClass = this.getClass();
    while (checkerClass != BaseTypeChecker.class) {
      final String classToLoad =
          checkerClass
              .getName()
              .replace("Checker", "AnnotatedTypeFactory")
              .replace("Subchecker", "AnnotatedTypeFactory");

      AnnotatedTypeFactory result =
          invokeConstructorFor(
              classToLoad,
              new Class<?>[] {checkerClass, CompilationUnitTree.class},
              new Object[] {this, root});
      if (result != null) return result;
      checkerClass = checkerClass.getSuperclass();
    }
    return new BasicAnnotatedTypeFactory<BaseTypeChecker>(this, root);
  }