private static boolean allSuperMethodsSelectedToDelete(
     List<PsiMethod> unselectedMethods, PsiMethod method) {
   final ArrayList<PsiMethod> superMethods =
       new ArrayList<>(Arrays.asList(method.findSuperMethods()));
   superMethods.retainAll(unselectedMethods);
   return superMethods.isEmpty();
 }
  public static SafeDeleteProcessor createInstance(
      Project project,
      @Nullable Runnable prepareSuccessfulCallBack,
      PsiElement[] elementsToDelete,
      boolean isSearchInComments,
      boolean isSearchNonJava,
      boolean askForAccessors) {
    ArrayList<PsiElement> elements = new ArrayList<PsiElement>(Arrays.asList(elementsToDelete));
    HashSet<PsiElement> elementsToDeleteSet =
        new HashSet<PsiElement>(Arrays.asList(elementsToDelete));

    for (PsiElement psiElement : elementsToDelete) {
      for (SafeDeleteProcessorDelegate delegate :
          Extensions.getExtensions(SafeDeleteProcessorDelegate.EP_NAME)) {
        if (delegate.handlesElement(psiElement)) {
          Collection<PsiElement> addedElements =
              delegate.getAdditionalElementsToDelete(
                  psiElement, elementsToDeleteSet, askForAccessors);
          if (addedElements != null) {
            elements.addAll(addedElements);
          }
          break;
        }
      }
    }

    return new SafeDeleteProcessor(
        project,
        prepareSuccessfulCallBack,
        PsiUtilCore.toPsiElementArray(elements),
        isSearchInComments,
        isSearchNonJava);
  }
 private static UsageInfo[] filterToBeDeleted(UsageInfo[] infos) {
   ArrayList<UsageInfo> list = new ArrayList<UsageInfo>();
   for (UsageInfo info : infos) {
     if (!(info instanceof SafeDeleteReferenceUsageInfo)
         || ((SafeDeleteReferenceUsageInfo) info).isSafeDelete()) {
       list.add(info);
     }
   }
   return list.toArray(new UsageInfo[list.size()]);
 }
 @Override
 public void run() {
   PsiDocumentManager.getInstance(myProject).commitAllDocuments();
   myUsageView.close();
   ArrayList<PsiElement> elements = new ArrayList<PsiElement>();
   for (SmartPsiElementPointer pointer : myPointers) {
     final PsiElement element = pointer.getElement();
     if (element != null) {
       elements.add(element);
     }
   }
   if (!elements.isEmpty()) {
     SafeDeleteHandler.invoke(myProject, PsiUtilCore.toPsiElementArray(elements), true);
   }
 }
 private static PsiMethod[] removeDeletedMethods(
     PsiMethod[] methods, final PsiElement[] allElementsToDelete) {
   ArrayList<PsiMethod> list = new ArrayList<>();
   for (PsiMethod method : methods) {
     boolean isDeleted = false;
     for (PsiElement element : allElementsToDelete) {
       if (element == method) {
         isDeleted = true;
         break;
       }
     }
     if (!isDeleted) {
       list.add(method);
     }
   }
   return list.toArray(new PsiMethod[list.size()]);
 }
  @Override
  protected boolean preprocessUsages(@NotNull Ref<UsageInfo[]> refUsages) {
    UsageInfo[] usages = refUsages.get();
    ArrayList<String> conflicts = new ArrayList<String>();

    for (PsiElement element : myElements) {
      for (SafeDeleteProcessorDelegate delegate :
          Extensions.getExtensions(SafeDeleteProcessorDelegate.EP_NAME)) {
        if (delegate.handlesElement(element)) {
          Collection<String> foundConflicts =
              delegate instanceof SafeDeleteProcessorDelegateBase
                  ? ((SafeDeleteProcessorDelegateBase) delegate)
                      .findConflicts(element, myElements, usages)
                  : delegate.findConflicts(element, myElements);
          if (foundConflicts != null) {
            conflicts.addAll(foundConflicts);
          }
          break;
        }
      }
    }

    final HashMap<PsiElement, UsageHolder> elementsToUsageHolders = sortUsages(usages);
    final Collection<UsageHolder> usageHolders = elementsToUsageHolders.values();
    for (UsageHolder usageHolder : usageHolders) {
      if (usageHolder.hasUnsafeUsagesInCode()) {
        conflicts.add(usageHolder.getDescription());
      }
    }

    if (!conflicts.isEmpty()) {
      final RefactoringEventData conflictData = new RefactoringEventData();
      conflictData.putUserData(RefactoringEventData.CONFLICTS_KEY, conflicts);
      myProject
          .getMessageBus()
          .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
          .conflictsDetected("refactoring.safeDelete", conflictData);
      if (ApplicationManager.getApplication().isUnitTestMode()) {
        if (!ConflictsInTestsException.isTestIgnore())
          throw new ConflictsInTestsException(conflicts);
      } else {
        UnsafeUsagesDialog dialog =
            new UnsafeUsagesDialog(ArrayUtil.toStringArray(conflicts), myProject);
        if (!dialog.showAndGet()) {
          final int exitCode = dialog.getExitCode();
          prepareSuccessful(); // dialog is always dismissed;
          if (exitCode == UnsafeUsagesDialog.VIEW_USAGES_EXIT_CODE) {
            showUsages(
                Arrays.stream(usages)
                    .filter(
                        usage ->
                            usage instanceof SafeDeleteReferenceUsageInfo
                                && !((SafeDeleteReferenceUsageInfo) usage).isSafeDelete())
                    .toArray(UsageInfo[]::new));
          }
          return false;
        } else {
          myPreviewNonCodeUsages = false;
        }
      }
    }

    UsageInfo[] preprocessedUsages = usages;
    for (SafeDeleteProcessorDelegate delegate :
        Extensions.getExtensions(SafeDeleteProcessorDelegate.EP_NAME)) {
      preprocessedUsages = delegate.preprocessUsages(myProject, preprocessedUsages);
      if (preprocessedUsages == null) return false;
    }
    final UsageInfo[] filteredUsages = UsageViewUtil.removeDuplicatedUsages(preprocessedUsages);
    prepareSuccessful(); // dialog is always dismissed
    if (filteredUsages == null) {
      return false;
    }
    refUsages.set(filteredUsages);
    return true;
  }
  @Nullable
  public UsageInfo[] preprocessUsages(final Project project, final UsageInfo[] usages) {
    final ArrayList<UsageInfo> result = new ArrayList<>();
    final ArrayList<UsageInfo> overridingMethods = new ArrayList<>();
    final ArrayList<SafeDeleteParameterCallHierarchyUsageInfo> delegatingParams = new ArrayList<>();
    final ArrayList<SafeDeleteMethodCalleeUsageInfo> calleesSafeToDelete = new ArrayList<>();
    for (UsageInfo usage : usages) {
      if (usage.isNonCodeUsage) {
        result.add(usage);
      } else if (usage instanceof SafeDeleteMethodCalleeUsageInfo) {
        calleesSafeToDelete.add((SafeDeleteMethodCalleeUsageInfo) usage);
      } else if (usage instanceof SafeDeleteOverridingMethodUsageInfo) {
        overridingMethods.add(usage);
      } else if (usage instanceof SafeDeleteParameterCallHierarchyUsageInfo) {
        delegatingParams.add((SafeDeleteParameterCallHierarchyUsageInfo) usage);
      } else if (usage instanceof SafeDeleteAnnotation) {
        result.add(
            new SafeDeleteAnnotation(
                (PsiAnnotation) usage.getElement(),
                ((SafeDeleteAnnotation) usage).getReferencedElement(),
                true));
      } else {
        result.add(usage);
      }
    }

    if (!overridingMethods.isEmpty()) {
      if (ApplicationManager.getApplication().isUnitTestMode()) {
        result.addAll(overridingMethods);
      } else {
        OverridingMethodsDialog dialog = new OverridingMethodsDialog(project, overridingMethods);
        if (!dialog.showAndGet()) {
          return null;
        }
        final ArrayList<UsageInfo> selected = dialog.getSelected();
        final Set<UsageInfo> unselected = new HashSet<>(overridingMethods);
        unselected.removeAll(selected);

        if (!unselected.isEmpty()) {
          final List<PsiMethod> unselectedMethods =
              ContainerUtil.map(
                  unselected,
                  info -> ((SafeDeleteOverridingMethodUsageInfo) info).getOverridingMethod());

          for (Iterator<UsageInfo> iterator = result.iterator(); iterator.hasNext(); ) {
            final UsageInfo info = iterator.next();
            if (info instanceof SafeDeleteOverrideAnnotation
                && !allSuperMethodsSelectedToDelete(
                    unselectedMethods, ((SafeDeleteOverrideAnnotation) info).getMethod())) {
              iterator.remove();
            }
          }
        }

        result.addAll(selected);
      }
    }

    if (!delegatingParams.isEmpty()) {
      final SafeDeleteParameterCallHierarchyUsageInfo parameterHierarchyUsageInfo =
          delegatingParams.get(0);
      if (ApplicationManager.getApplication().isUnitTestMode()) {
        result.addAll(delegatingParams);
      } else {
        final PsiMethod method = parameterHierarchyUsageInfo.getCalledMethod();
        final PsiParameter parameter = parameterHierarchyUsageInfo.getReferencedElement();
        final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
        final JavaCallerChooser chooser =
            new SafeDeleteJavaCallerChooser(method, project, result) {
              @Override
              protected ArrayList<SafeDeleteParameterCallHierarchyUsageInfo> getTopLevelItems() {
                return delegatingParams;
              }

              @Override
              protected int getParameterIdx() {
                return parameterIndex;
              }
            };
        TreeUtil.expand(chooser.getTree(), 2);
        if (!chooser.showAndGet()) {
          return null;
        }
      }
    }

    if (!calleesSafeToDelete.isEmpty()) {
      if (ApplicationManager.getApplication().isUnitTestMode()) {
        result.addAll(calleesSafeToDelete);
      } else {
        final PsiMethod method = calleesSafeToDelete.get(0).getCallerMethod();
        final ArrayList<UsageInfo> list = new ArrayList<>();
        JavaCallerChooser chooser =
            new SafeDeleteJavaCalleeChooser(method, project, list) {
              @Override
              protected ArrayList<SafeDeleteMethodCalleeUsageInfo> getTopLevelItems() {
                return calleesSafeToDelete;
              }
            };
        TreeUtil.expand(chooser.getTree(), 2);
        if (!chooser.showAndGet()) {
          return null;
        }
        result.addAll(list);
        final List<PsiElement> methodsToDelete = new ArrayList<>();
        for (UsageInfo info : list) {
          methodsToDelete.add(info.getElement());
        }
        methodsToDelete.add(method);
        final Condition<PsiElement> insideDeletedCondition =
            getUsageInsideDeletedFilter(
                methodsToDelete.toArray(new PsiElement[methodsToDelete.size()]));
        for (UsageInfo info : list) {
          SafeDeleteProcessor.addNonCodeUsages(
              info.getElement(),
              result,
              insideDeletedCondition,
              JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD,
              JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD);
        }
      }
    }

    return result.toArray(new UsageInfo[result.size()]);
  }
  @Nullable
  @Override
  public Collection<? extends PsiElement> getElementsToSearch(
      @NotNull PsiElement element,
      @Nullable Module module,
      @NotNull Collection<PsiElement> allElementsToDelete) {
    Project project = element.getProject();
    if (element instanceof PsiPackage && module != null) {
      final PsiDirectory[] directories =
          ((PsiPackage) element).getDirectories(module.getModuleScope());
      if (directories.length == 0) return null;
      return Arrays.asList(directories);
    } else if (element instanceof PsiMethod) {
      final PsiMethod[] methods =
          SuperMethodWarningUtil.checkSuperMethods(
              (PsiMethod) element,
              RefactoringBundle.message("to.delete.with.usage.search"),
              allElementsToDelete);
      if (methods.length == 0) return null;
      final ArrayList<PsiMethod> psiMethods = new ArrayList<>(Arrays.asList(methods));
      psiMethods.add((PsiMethod) element);
      return psiMethods;
    } else if (element instanceof PsiParameter
        && ((PsiParameter) element).getDeclarationScope() instanceof PsiMethod) {
      PsiMethod method = (PsiMethod) ((PsiParameter) element).getDeclarationScope();
      final Set<PsiParameter> parametersToDelete = new HashSet<>();
      parametersToDelete.add((PsiParameter) element);
      final int parameterIndex =
          method.getParameterList().getParameterIndex((PsiParameter) element);
      final List<PsiMethod> superMethods =
          new ArrayList<>(Arrays.asList(method.findDeepestSuperMethods()));
      if (superMethods.isEmpty()) {
        superMethods.add(method);
      }
      for (PsiMethod superMethod : superMethods) {
        parametersToDelete.add(superMethod.getParameterList().getParameters()[parameterIndex]);
        OverridingMethodsSearch.search(superMethod)
            .forEach(
                overrider -> {
                  parametersToDelete.add(
                      overrider.getParameterList().getParameters()[parameterIndex]);
                  return true;
                });
      }

      if (parametersToDelete.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode()) {
        String message =
            RefactoringBundle.message(
                "0.is.a.part.of.method.hierarchy.do.you.want.to.delete.multiple.parameters",
                UsageViewUtil.getLongName(method));
        int result =
            Messages.showYesNoCancelDialog(
                project, message, SafeDeleteHandler.REFACTORING_NAME, Messages.getQuestionIcon());
        if (result == Messages.CANCEL) return null;
        if (result == Messages.NO) return Collections.singletonList(element);
      }
      return parametersToDelete;
    } else if (element instanceof PsiTypeParameter) {
      final PsiTypeParameterListOwner owner = ((PsiTypeParameter) element).getOwner();
      if (owner instanceof PsiMethod && !owner.hasModifierProperty(PsiModifier.STATIC)) {
        final PsiTypeParameterList typeParameterList = owner.getTypeParameterList();
        if (typeParameterList != null) {
          final int index = typeParameterList.getTypeParameterIndex((PsiTypeParameter) element);
          if (index >= 0) {
            final ArrayList<PsiTypeParameter> overriders = new ArrayList<>();
            overriders.add((PsiTypeParameter) element);
            OverridingMethodsSearch.search((PsiMethod) owner)
                .forEach(
                    overrider -> {
                      final PsiTypeParameter[] typeParameters = overrider.getTypeParameters();
                      if (index < typeParameters.length) {
                        overriders.add(typeParameters[index]);
                      }
                      return true;
                    });
            if (overriders.size() > 1) {
              String message =
                  RefactoringBundle.message(
                      "0.is.a.part.of.method.hierarchy.do.you.want.to.delete.multiple.type.parameters",
                      UsageViewUtil.getLongName(owner));
              int result =
                  ApplicationManager.getApplication().isUnitTestMode()
                      ? Messages.YES
                      : Messages.showYesNoCancelDialog(
                          project,
                          message,
                          SafeDeleteHandler.REFACTORING_NAME,
                          Messages.getQuestionIcon());
              if (result == Messages.CANCEL) return null;
              if (result == Messages.YES) return overriders;
            }
          }
        }
      }
    }

    return Collections.singletonList(element);
  }