docs: public static void main(String[] args) throws IOException, CheckedAnalysisException, InterruptedException {
          if (args.length != 2) {
              System.err.println("Usage: " + DiscoverSourceDirectories.class.getName() + " <project file> <root source dir>");
              System.exit(1);
          }
  
          Project project = Project.readProject(args[0]);
  
          IErrorLogger errorLogger = new IErrorLogger() {
  
              @Override
              public void reportMissingClass(ClassNotFoundException ex) {
                  String className = ClassNotFoundExceptionParser.getMissingClassName(ex);
                  if (className != null) {
                      logError("Missing class: " + className);
                  } else {
                      logError("Missing class: " + ex);
                  }
              }
  
              @Override
              public void reportMissingClass(ClassDescriptor classDescriptor) {
                  logError("Missing class: " + classDescriptor.toDottedClassName());
              }
  
              @Override
              public void logError(String message) {
                  System.err.println("Error: " + message);
              }
  
              @Override
              public void logError(String message, Throwable e) {
                  logError(message + ": " + e.getMessage());
              }
  
              @Override
              public void reportSkippedAnalysis(MethodDescriptor method) {
                  logError("Skipped analysis of method " + method.toString());
              }
  
          };
  
          DiscoverSourceDirectories.Progress progress = new DiscoverSourceDirectories.Progress() {
  
              @Override
              public void startRecursiveDirectorySearch() {
                  System.out.print("Scanning directories...");
                  System.out.flush();
              }
  
              @Override
              public void doneRecursiveDirectorySearch() {
                  System.out.println("done");
              }
  
              @Override
              public void startScanningArchives(int numArchivesToScan) {
                  System.out.print("Scanning " + numArchivesToScan + " archives..");
                  System.out.flush();
              }
  
              @Override
              public void doneScanningArchives() {
                  System.out.println("done");
              }
  
              @Override
              public void startScanningClasses(int numClassesToScan) {
                  System.out.print("Scanning " + numClassesToScan + " classes...");
                  System.out.flush();
              }
  
              @Override
              public void finishClass() {
                  System.out.print(".");
                  System.out.flush();
              }
  
              @Override
              public void doneScanningClasses() {
                  System.out.println("done");
              }
  
              @Override
              public void finishArchive() {
                  System.out.print(".");
                  System.out.flush();
              }
  
              @Override
              public void startArchive(String name) {
                  // noop
              }
          };
  
          DiscoverSourceDirectories discoverSourceDirectories = new DiscoverSourceDirectories();
          discoverSourceDirectories.setProject(project);
          discoverSourceDirectories.setRootSourceDirectory(args[1]);
          discoverSourceDirectories.setErrorLogger(errorLogger);
          discoverSourceDirectories.setProgress(progress);
  
          discoverSourceDirectories.execute();
  
          System.out.println("Found source directories:");
          for (String srcDir : discoverSourceDirectories.getDiscoveredSourceDirectoryList()) {
              System.out.println("  " + srcDir);
          }
      }