private static void generateVisitorClass(final String visitorName,
                                           final PsiClass baseClass,
                                           final PsiDirectory directory,
                                           final GlobalSearchScope scope) {

    final Map<PsiClass, Set<PsiClass>> classes = new HashMap<>();
    for (PsiClass aClass : ClassInheritorsSearch.search(baseClass, scope, true).findAll()) {
      if (aClass.hasModifierProperty(PsiModifier.ABSTRACT) == baseClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
        final List<PsiClass> implementors =
          ContainerUtil.findAll(ClassInheritorsSearch.search(aClass).findAll(),
                                psiClass -> !psiClass.hasModifierProperty(PsiModifier.ABSTRACT));
        classes.put(aClass, new HashSet<>(implementors));
      }
    }
    final Map<PsiClass, Set<PsiClass>> pathMap = new HashMap<>();
    for (PsiClass aClass : classes.keySet()) {
      final Set<PsiClass> superClasses = new LinkedHashSet<>();
      for (PsiClass superClass : aClass.getSupers()) {
        if (superClass.isInheritor(baseClass, true)) {
          superClasses.add(superClass);
          final Set<PsiClass> superImplementors = classes.get(superClass);
          if (superImplementors != null) {
            superImplementors.removeAll(classes.get(aClass));
          }
        }
      }
      if (superClasses.isEmpty()) {
        superClasses.add(baseClass);
      }
      pathMap.put(aClass, superClasses);
    }
    pathMap.put(baseClass, Collections.emptySet());
    final ArrayList<PsiFile> psiFiles = new ArrayList<>();
    for (Set<PsiClass> implementors : classes.values()) {
      for (PsiClass psiClass : implementors) {
        psiFiles.add(psiClass.getContainingFile());
      }
    }
    final Project project = baseClass.getProject();
    final PsiClass visitorClass = JavaPsiFacade.getInstance(project).findClass(visitorName, GlobalSearchScope.projectScope(project));
    if (visitorClass != null) {
      psiFiles.add(visitorClass.getContainingFile());
    }
    final int finalDetectedPrefix = detectClassPrefix(classes.keySet()).length();
    try {
      WriteCommandAction.writeCommandAction(project, PsiUtilCore.toPsiFileArray(psiFiles))
                        .withGlobalUndo()
                        .run(() -> {
                          if (visitorClass == null) {
                            final String shortClassName = PsiNameHelper.getShortClassName(visitorName);
                            if (directory != null) {
                              final PsiClass vc = JavaDirectoryService.getInstance().createClass(directory, shortClassName);
                              generateVisitorClass(vc, classes, pathMap, finalDetectedPrefix);
                            }
                          }
                          else {
                            generateVisitorClass(visitorClass, classes, pathMap, finalDetectedPrefix);
                          }
                        });
    }
    catch (Throwable throwable) {
      throw new RuntimeException(throwable);
    }
  }
private static void generateVisitorClass(final String visitorName,
                                           final PsiClass baseClass,
                                           final PsiDirectory directory,
                                           final GlobalSearchScope scope)