@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()]); }
private static Set<PsiMethod> validateOverridingMethods( PsiMethod originalMethod, final Collection<PsiReference> originalReferences, Collection<PsiMethod> overridingMethods, HashMap<PsiMethod, Collection<PsiReference>> methodToReferences, List<UsageInfo> usages, final PsiElement[] allElementsToDelete) { Set<PsiMethod> validOverriding = new LinkedHashSet<>(overridingMethods); Set<PsiMethod> multipleInterfaceImplementations = new HashSet<>(); boolean anyNewBadRefs; do { anyNewBadRefs = false; for (PsiMethod overridingMethod : overridingMethods) { if (validOverriding.contains(overridingMethod)) { final Collection<PsiReference> overridingReferences = methodToReferences.get(overridingMethod); boolean anyOverridingRefs = false; for (final PsiReference overridingReference : overridingReferences) { final PsiElement element = overridingReference.getElement(); if (!isInside(element, allElementsToDelete) && !isInside(element, validOverriding)) { anyOverridingRefs = true; break; } } if (!anyOverridingRefs && isMultipleInterfacesImplementation( overridingMethod, originalMethod, allElementsToDelete)) { anyOverridingRefs = true; multipleInterfaceImplementations.add(overridingMethod); } if (anyOverridingRefs) { validOverriding.remove(overridingMethod); anyNewBadRefs = true; for (PsiReference reference : originalReferences) { final PsiElement element = reference.getElement(); if (!isInside(element, allElementsToDelete) && !isInside(element, overridingMethods)) { usages.add( new SafeDeleteReferenceJavaDeleteUsageInfo(element, originalMethod, false)); validOverriding.clear(); } } } } } } while (anyNewBadRefs && !validOverriding.isEmpty()); for (PsiMethod method : validOverriding) { if (method != originalMethod) { usages.add(new SafeDeleteOverridingMethodUsageInfo(method, originalMethod)); } } for (PsiMethod method : overridingMethods) { if (!validOverriding.contains(method) && !multipleInterfaceImplementations.contains(method) && canBePrivate( method, methodToReferences.get(method), validOverriding, allElementsToDelete)) { usages.add(new SafeDeletePrivatizeMethod(method, originalMethod)); } else { usages.add(new SafeDeleteOverrideAnnotation(method, originalMethod)); } } return validOverriding; }
@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); }