@NotNull private PsiElement[] findSuperElements(@NotNull PsiFile file, int offset) { PsiElement element = getElement(file, offset); if (element == null) return PsiElement.EMPTY_ARRAY; final PsiElement psiElement = PsiTreeUtil.getParentOfType(element, PsiFunctionalExpression.class, PsiMember.class); if (psiElement instanceof PsiFunctionalExpression) { final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(psiElement); if (interfaceMethod != null) { return ArrayUtil.prepend(interfaceMethod, interfaceMethod.findSuperMethods(false)); } } final PsiNameIdentifierOwner parent = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class, PsiClass.class); if (parent == null) { return PsiElement.EMPTY_ARRAY; } return FindSuperElementsHelper.findSuperElements(parent); }
private static void collectSiblingInheritedMethods(@NotNull final Collection<PsiMethod> methods, @NotNull Collection<LineMarkerInfo> result, @NotNull Map<PsiClass, PsiClass> subClassCache) { for (PsiMethod method : methods) { ProgressManager.checkCanceled(); PsiClass aClass = method.getContainingClass(); if (aClass == null || aClass.hasModifierProperty(PsiModifier.FINAL) || aClass.isInterface()) continue; boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.hasModifierProperty(PsiModifier.STATIC) && method.hasModifierProperty(PsiModifier.PUBLIC)&& !method.hasModifierProperty(PsiModifier.FINAL)&& !method.hasModifierProperty(PsiModifier.NATIVE); if (!canHaveSiblingSuper) continue; PsiMethod siblingInheritedViaSubClass = Pair.getFirst(FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, subClassCache)); if (siblingInheritedViaSubClass == null) { continue; } PsiElement range = getMethodRange(method); ArrowUpLineMarkerInfo upInfo = new ArrowUpLineMarkerInfo(range, AllIcons.Gutter.ImplementingMethod, MarkerType.SIBLING_OVERRIDING_METHOD, Pass.UPDATE_OVERRIDEN_MARKERS); LineMarkerInfo info = NavigateAction.setNavigateAction(upInfo, "Go to super method", IdeActions.ACTION_GOTO_SUPER); result.add(info); } }
@Nullable private static String calculateOverridingSiblingMethodTooltip(@NotNull PsiMethod method) { Pair<PsiMethod, PsiClass> pair = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, FindSuperElementsHelper.createSubClassCache()); if (pair == null) return null; PsiMethod superMethod = pair.getFirst(); PsiClass subClass = pair.getSecond(); boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); boolean isSuperAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT); String postfix = MessageFormat.format(" via sub-class <a href=\"#javaClass/{0}\">{0}</a>", ClassPresentationUtil.getNameForClass(subClass, true)); @NonNls String pattern = DaemonBundle.message(isSuperAbstract && !isAbstract ? "method.implements" : "method.overrides") + postfix; return composeText(new PsiElement[]{superMethod}, "", pattern, IdeActions.ACTION_GOTO_SUPER); }
private boolean exposedInInterface(PsiMethod method) { PsiMethod[] superMethods = method.findSuperMethods(); PsiMethod siblingInherited = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); if (siblingInherited != null && !ArrayUtil.contains(siblingInherited, superMethods)) { superMethods = ArrayUtil.append(superMethods, siblingInherited); } for (final PsiMethod superMethod : superMethods) { final PsiClass superClass = superMethod.getContainingClass(); if (superClass == null) { continue; } if (superClass.isInterface()) { return true; } final String superclassName = superClass.getQualifiedName(); if (CommonClassNames.JAVA_LANG_OBJECT.equals(superclassName)) { return true; } if (exposedInInterface(superMethod)) { return true; } } return false; }
@Override public void collectSlowLineMarkers(@NotNull final List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) { Set<PsiMethod> methods = new HashSet<PsiMethod>(); Map<PsiClass, PsiClass> subClassCache = FindSuperElementsHelper.createSubClassCache(); for (PsiElement element : elements) { ProgressManager.checkCanceled(); if (element instanceof GrField) { methods.addAll(GroovyPropertyUtils.getFieldAccessors((GrField)element)); } else if (element instanceof GrMethod) { GrReflectedMethod[] reflected = ((GrMethod)element).getReflectedMethods(); if (reflected.length != 0) { Collections.addAll(methods, reflected); } else { methods.add((PsiMethod)element); } } else if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) { JavaLineMarkerProvider.collectInheritingClasses((PsiClass)element, result, subClassCache); } } collectOverridingMethods(methods, result); }
@Nullable private static String calculateOverridingSiblingMethodTooltip(@NotNull PsiMethod method) { FindSuperElementsHelper.SiblingInfo pair = FindSuperElementsHelper.getSiblingInfoInheritedViaSubClass(method); if(pair == null) { return null; } PsiMethod superMethod = pair.superMethod; PsiClass subClass = pair.subClass; boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); boolean isSuperAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT); String postfix = MessageFormat.format(" via sub-class <a href=\"#javaClass/{0}\">{0}</a>", ClassPresentationUtil.getNameForClass(subClass, true)); @NonNls String pattern = DaemonBundle.message(isSuperAbstract && !isAbstract ? "method.implements" : "method.overrides") + postfix; return composeText(new PsiElement[]{superMethod}, "", pattern, IdeActions.ACTION_GOTO_SUPER); }
public static boolean isMethodReferenced(@NotNull Project project, @NotNull PsiFile containingFile, @NotNull PsiMethod method, @NotNull ProgressIndicator progress, @NotNull GlobalUsageHelper helper) { if (helper.isLocallyUsed(method)) return true; boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE); PsiClass containingClass = method.getContainingClass(); if (JavaHighlightUtil.isSerializationRelatedMethod(method, containingClass)) return true; if (isPrivate) { if (isIntentionalPrivateConstructor(method, containingClass)) { return true; } if (isImplicitUsage(project, method, progress)) { return true; } if (!helper.isCurrentFileAlreadyChecked()) { return !weAreSureThereAreNoUsages(project, containingFile, method, progress, helper); } } else { //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too boolean isConstructor = method.isConstructor(); if (containingClass != null && isConstructor && containingClass.getConstructors().length == 1 && isClassUsed(project, containingFile, containingClass, progress, helper)) { return true; } if (isImplicitUsage(project, method, progress)) return true; if (!isConstructor && FindSuperElementsHelper.findSuperElements(method).length != 0) { return true; } if (!weAreSureThereAreNoUsages(project, containingFile, method, progress, helper)) { return true; } } return false; }
@NotNull private static PsiElement[] findSuperElements(final PsiElement element) { PsiNameIdentifierOwner parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class); if (parent == null) { return PsiElement.EMPTY_ARRAY; } return FindSuperElementsHelper.findSuperElements(parent); }
@Override public void collectSlowLineMarkers(@NotNull final List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) { ApplicationManager.getApplication().assertReadAccessAllowed(); Map<PsiClass, PsiClass> subClassCache = FindSuperElementsHelper.createSubClassCache(); Collection<PsiMethod> methods = new THashSet<PsiMethod>(); //noinspection ForLoopReplaceableByForEach for (int i = 0; i < elements.size(); i++) { PsiElement element = elements.get(i); ProgressManager.checkCanceled(); if (!(element instanceof PsiIdentifier)) continue; PsiElement parent = element.getParent(); if (parent instanceof PsiMethod) { final PsiMethod method = (PsiMethod)parent; if (PsiUtil.canBeOverriden(method)) { methods.add(method); } } else if (parent instanceof PsiClass && !(parent instanceof PsiTypeParameter)) { collectInheritingClasses((PsiClass)parent, result, subClassCache); } } if (!methods.isEmpty()) { collectOverridingMethods(methods, result); collectSiblingInheritedMethods(methods, result, subClassCache); } }
private static void navigateToSiblingOverridingMethod(MouseEvent e, @NotNull PsiMethod method) { PsiMethod superMethod = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); if (superMethod == null) return; PsiElementListNavigator.openTargets(e, new NavigatablePsiElement[]{superMethod}, DaemonBundle.message("navigation.title.super.method", method.getName()), DaemonBundle.message("navigation.findUsages.title.super.method", method.getName()), new MethodCellRenderer(false)); }
@NotNull private static PsiMethod[] composeSuperMethods(@NotNull PsiMethod method, boolean acceptSelf) { PsiElement[] superElements = FindSuperElementsHelper.findSuperElements(method); PsiMethod[] superMethods = ContainerUtil.map(superElements, new Function<PsiElement, PsiMethod>() { @Override public PsiMethod fun(PsiElement element) { return (PsiMethod)element; } }, PsiMethod.EMPTY_ARRAY); if (acceptSelf) { superMethods = ArrayUtil.prepend(method, superMethods); } return superMethods; }
@Override public void visitMethod(@NotNull PsiMethod method) { if (method.isConstructor()) { return; } final PsiClass containingClass = method.getContainingClass(); if (containingClass == null) { return; } if (containingClass.isInterface() || containingClass.isAnnotationType()) { return; } if (!containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) { return; } if (method.hasModifierProperty(PsiModifier.ABSTRACT) || method.hasModifierProperty(PsiModifier.NATIVE) || method.hasModifierProperty(PsiModifier.FINAL)) { return; } if (!MethodUtils.isEmpty(method)) { return; } if (FindSuperElementsHelper.getSiblingInheritedViaSubClass(method) != null) { // it may be an explicit intention to have non-abstract method here in order to sibling-inherit the method in subclass return; } registerMethodError(method); }
@Nullable private static PsiElement[] findSuperElements(final PsiElement element) { PsiNameIdentifierOwner parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class); if (parent == null) { return null; } return FindSuperElementsHelper.findSuperElements(parent); }
@Nullable private PsiElement[] findSuperElements(PsiFile file, int offset) { PsiNameIdentifierOwner parent = getElement(file, offset); if (parent == null) return null; return FindSuperElementsHelper.findSuperElements(parent); }
private static List<LineMarkerInfo> collectSiblingInheritedMethods(@NotNull final Collection<PsiMethod> methods) { Map<PsiMethod, FindSuperElementsHelper.SiblingInfo> map = FindSuperElementsHelper.getSiblingInheritanceInfos(methods); return ContainerUtil.map(map.keySet(), method -> { PsiElement range = getMethodRange(method); ArrowUpLineMarkerInfo upInfo = new ArrowUpLineMarkerInfo(range, AllIcons.Gutter.SiblingInheritedMethod, MarkerType.SIBLING_OVERRIDING_METHOD, Pass.LINE_MARKERS); return NavigateAction.setNavigateAction(upInfo, "Go to super method", IdeActions.ACTION_GOTO_SUPER); }); }
private static void navigateToSiblingOverridingMethod(MouseEvent e, @NotNull PsiMethod method) { PsiMethod superMethod = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); if(superMethod == null) { return; } PsiElementListNavigator.openTargets(e, new NavigatablePsiElement[]{superMethod}, DaemonBundle.message("navigation.title.super.method", method.getName()), DaemonBundle.message("navigation" + ".findUsages.title.super.method", method.getName()), new MethodCellRenderer(false)); }
@NotNull private static PsiMethod[] composeSuperMethods(@NotNull PsiMethod method, boolean acceptSelf) { PsiElement[] superElements = FindSuperElementsHelper.findSuperElements(method); PsiMethod[] superMethods = ContainerUtil.map(superElements, element -> (PsiMethod) element, PsiMethod.EMPTY_ARRAY); if(acceptSelf) { superMethods = ArrayUtil.prepend(method, superMethods); } return superMethods; }
@NotNull public static PsiMethod[] checkSuperMethods(@NotNull PsiMethod method, @NotNull String actionString, @NotNull Collection<PsiElement> ignore) { PsiClass aClass = method.getContainingClass(); if (aClass == null) return new PsiMethod[]{method}; final Collection<PsiMethod> superMethods = DeepestSuperMethodsSearch.search(method).findAll(); superMethods.removeAll(ignore); if (superMethods.isEmpty()) { PsiMethod siblingSuperMethod = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); if (siblingSuperMethod != null) { superMethods.add(siblingSuperMethod); } } if (superMethods.isEmpty()) return new PsiMethod[]{method}; Set<String> superClasses = new HashSet<String>(); boolean superAbstract = false; boolean parentInterface = false; for (final PsiMethod superMethod : superMethods) { final PsiClass containingClass = superMethod.getContainingClass(); superClasses.add(containingClass.getQualifiedName()); final boolean isInterface = containingClass.isInterface(); superAbstract |= isInterface || superMethod.hasModifierProperty(PsiModifier.ABSTRACT); parentInterface |= isInterface; } SuperMethodWarningDialog dialog = new SuperMethodWarningDialog(method.getProject(), DescriptiveNameUtil.getDescriptiveName(method), actionString, superAbstract, parentInterface, aClass.isInterface(), ArrayUtil.toStringArray(superClasses)); dialog.show(); if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { return superMethods.toArray(new PsiMethod[superMethods.size()]); } if (dialog.getExitCode() == SuperMethodWarningDialog.NO_EXIT_CODE) { return new PsiMethod[]{method}; } return PsiMethod.EMPTY_ARRAY; }
public static boolean isMethodReferenced(@NotNull Project project, @NotNull PsiFile containingFile, @NotNull PsiMethod method, @NotNull ProgressIndicator progress, @NotNull GlobalUsageHelper helper) { if(helper.isLocallyUsed(method)) { return true; } boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE); PsiClass containingClass = method.getContainingClass(); if(JavaHighlightUtil.isSerializationRelatedMethod(method, containingClass)) { return true; } if(isPrivate) { if(isIntentionalPrivateConstructor(method, containingClass)) { return true; } if(isImplicitUsage(project, method, progress)) { return true; } if(!helper.isCurrentFileAlreadyChecked()) { return !weAreSureThereAreNoUsages(project, containingFile, method, progress, helper); } } else { //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too boolean isConstructor = method.isConstructor(); if(containingClass != null && isConstructor && containingClass.getConstructors().length == 1 && isClassUsed(project, containingFile, containingClass, progress, helper)) { return true; } if(isImplicitUsage(project, method, progress)) { return true; } if(!isConstructor && FindSuperElementsHelper.findSuperElements(method).length != 0) { return true; } if(!weAreSureThereAreNoUsages(project, containingFile, method, progress, helper)) { return true; } } return false; }