| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.find.findUsages; |
| |
| import com.intellij.CommonBundle; |
| import com.intellij.find.FindBundle; |
| import com.intellij.ide.util.SuperMethodWarningUtil; |
| import com.intellij.openapi.actionSystem.DataContext; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ReadActionProcessor; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.pom.PomTarget; |
| import com.intellij.pom.references.PomService; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.codeStyle.VariableKind; |
| import com.intellij.psi.impl.search.ThrowSearchUtil; |
| import com.intellij.psi.meta.PsiMetaData; |
| import com.intellij.psi.meta.PsiMetaOwner; |
| import com.intellij.psi.search.*; |
| import com.intellij.psi.search.searches.*; |
| import com.intellij.psi.targets.AliasingPsiTarget; |
| import com.intellij.psi.targets.AliasingPsiTargetMapper; |
| import com.intellij.psi.util.MethodSignature; |
| import com.intellij.psi.util.PropertyUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.PsiUtilCore; |
| import com.intellij.psi.xml.XmlAttributeValue; |
| import com.intellij.refactoring.util.JavaNonCodeSearchElementDescriptionProvider; |
| import com.intellij.refactoring.util.NonCodeSearchDescriptionLocation; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.ContainerUtil; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| /** |
| * @author peter |
| */ |
| public class JavaFindUsagesHandler extends FindUsagesHandler{ |
| private static final Logger LOG = Logger.getInstance("#com.intellij.find.findUsages.JavaFindUsagesHandler"); |
| public static final String ACTION_STRING = FindBundle.message("find.super.method.warning.action.verb"); |
| |
| private final PsiElement[] myElementsToSearch; |
| private final JavaFindUsagesHandlerFactory myFactory; |
| |
| public JavaFindUsagesHandler(@NotNull PsiElement psiElement, @NotNull JavaFindUsagesHandlerFactory factory) { |
| this(psiElement, PsiElement.EMPTY_ARRAY, factory); |
| } |
| |
| public JavaFindUsagesHandler(@NotNull PsiElement psiElement, @NotNull PsiElement[] elementsToSearch, @NotNull JavaFindUsagesHandlerFactory factory) { |
| super(psiElement); |
| myElementsToSearch = elementsToSearch; |
| myFactory = factory; |
| } |
| |
| @Override |
| @NotNull |
| public AbstractFindUsagesDialog getFindUsagesDialog(boolean isSingleFile, boolean toShowInNewTab, boolean mustOpenInNewTab) { |
| PsiElement element = getPsiElement(); |
| if (element instanceof PsiPackage) { |
| return new FindPackageUsagesDialog(element, getProject(), myFactory.getFindPackageOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); |
| } |
| if (element instanceof PsiClass) { |
| return new FindClassUsagesDialog(element, getProject(), myFactory.getFindClassOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); |
| } |
| if (element instanceof PsiMethod) { |
| return new FindMethodUsagesDialog(element, getProject(), myFactory.getFindMethodOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); |
| } |
| if (element instanceof PsiVariable) { |
| return new FindVariableUsagesDialog(element, getProject(), myFactory.getFindVariableOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); |
| } |
| if (ThrowSearchUtil.isSearchable(element)) { |
| return new FindThrowUsagesDialog(element, getProject(), myFactory.getFindThrowOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); |
| } |
| return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab); |
| } |
| |
| private static boolean askWhetherShouldSearchForParameterInOverridingMethods(@NotNull PsiElement psiElement, @NotNull PsiParameter parameter) { |
| return Messages.showOkCancelDialog(psiElement.getProject(), |
| FindBundle.message("find.parameter.usages.in.overriding.methods.prompt", parameter.getName()), |
| FindBundle.message("find.parameter.usages.in.overriding.methods.title"), |
| CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText(), |
| Messages.getQuestionIcon()) == Messages.OK; |
| } |
| |
| @NotNull |
| private static PsiElement[] getParameterElementsToSearch(@NotNull PsiParameter parameter) { |
| final PsiMethod method = (PsiMethod)parameter.getDeclarationScope(); |
| PsiMethod[] overrides = OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY); |
| for (int i = 0; i < overrides.length; i++) { |
| overrides[i] = (PsiMethod)overrides[i].getNavigationElement(); |
| } |
| List<PsiElement> elementsToSearch = new ArrayList<PsiElement>(overrides.length + 1); |
| elementsToSearch.add(parameter); |
| int idx = method.getParameterList().getParameterIndex(parameter); |
| for (PsiMethod override : overrides) { |
| final PsiParameter[] parameters = override.getParameterList().getParameters(); |
| if (idx < parameters.length) { |
| elementsToSearch.add(parameters[idx]); |
| } |
| } |
| return PsiUtilCore.toPsiElementArray(elementsToSearch); |
| } |
| |
| |
| @Override |
| @NotNull |
| public PsiElement[] getPrimaryElements() { |
| final PsiElement element = getPsiElement(); |
| if (element instanceof PsiParameter) { |
| final PsiParameter parameter = (PsiParameter)element; |
| final PsiElement scope = parameter.getDeclarationScope(); |
| if (scope instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)scope; |
| if (PsiUtil.canBeOverriden(method)) { |
| final PsiClass aClass = method.getContainingClass(); |
| LOG.assertTrue(aClass != null); //Otherwise can not be overriden |
| |
| boolean hasOverridden = OverridingMethodsSearch.search(method).findFirst() != null; |
| if (hasOverridden && askWhetherShouldSearchForParameterInOverridingMethods(element, parameter)) { |
| return getParameterElementsToSearch(parameter); |
| } |
| } |
| } |
| } |
| return myElementsToSearch.length == 0 ? new PsiElement[]{element} : myElementsToSearch; |
| } |
| |
| @Override |
| @NotNull |
| public PsiElement[] getSecondaryElements() { |
| PsiElement element = getPsiElement(); |
| if (ApplicationManager.getApplication().isUnitTestMode()) return PsiElement.EMPTY_ARRAY; |
| if (element instanceof PsiField) { |
| final PsiField field = (PsiField)element; |
| PsiClass containingClass = field.getContainingClass(); |
| if (containingClass != null) { |
| String fieldName = field.getName(); |
| final String propertyName = JavaCodeStyleManager.getInstance(getProject()).variableNameToPropertyName(fieldName, VariableKind.FIELD); |
| Set<PsiMethod> accessors = new THashSet<PsiMethod>(); |
| boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); |
| PsiMethod getter = PropertyUtil.findPropertyGetterWithType(propertyName, isStatic, field.getType(), |
| ContainerUtil.iterate(containingClass.getMethods())); |
| if (getter != null) accessors.add(getter); |
| PsiMethod setter = PropertyUtil.findPropertySetterWithType(propertyName, isStatic, field.getType(), |
| ContainerUtil.iterate(containingClass.getMethods())); |
| if (setter != null) accessors.add(setter); |
| accessors.addAll(PropertyUtil.getAccessors(containingClass, fieldName)); |
| if (!accessors.isEmpty()) { |
| boolean containsPhysical = ContainerUtil.find(accessors, new Condition<PsiMethod>() { |
| @Override |
| public boolean value(PsiMethod psiMethod) { |
| return psiMethod.isPhysical(); |
| } |
| }) != null; |
| final boolean doSearch = !containsPhysical || |
| Messages.showOkCancelDialog(FindBundle.message("find.field.accessors.prompt", fieldName), |
| FindBundle.message("find.field.accessors.title"), |
| CommonBundle.getYesButtonText(), |
| CommonBundle.getNoButtonText(), Messages.getQuestionIcon()) == |
| Messages.OK; |
| if (doSearch) { |
| final Set<PsiElement> elements = new THashSet<PsiElement>(); |
| for (PsiMethod accessor : accessors) { |
| ContainerUtil.addAll(elements, SuperMethodWarningUtil.checkSuperMethods(accessor, ACTION_STRING)); |
| } |
| return PsiUtilCore.toPsiElementArray(elements); |
| } |
| } |
| } |
| } |
| return super.getSecondaryElements(); |
| } |
| |
| @Override |
| @NotNull |
| public FindUsagesOptions getFindUsagesOptions(@Nullable final DataContext dataContext) { |
| PsiElement element = getPsiElement(); |
| if (element instanceof PsiPackage) { |
| return myFactory.getFindPackageOptions(); |
| } |
| if (element instanceof PsiClass) { |
| return myFactory.getFindClassOptions(); |
| } |
| if (element instanceof PsiMethod) { |
| return myFactory.getFindMethodOptions(); |
| } |
| if (element instanceof PsiVariable) { |
| return myFactory.getFindVariableOptions(); |
| } |
| if (ThrowSearchUtil.isSearchable(element)) { |
| return myFactory.getFindThrowOptions(); |
| } |
| return super.getFindUsagesOptions(dataContext); |
| } |
| |
| @Override |
| protected Set<String> getStringsToSearch(@NotNull final PsiElement element) { |
| if (element instanceof PsiDirectory) { // normalize a directory to a corresponding package |
| PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)element); |
| return aPackage == null ? Collections.<String>emptySet() : getStringsToSearch(aPackage); |
| } |
| |
| final Set<String> result = new HashSet<String>(); |
| |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| if (element instanceof PsiPackage) { |
| ContainerUtil.addIfNotNull(result, ((PsiPackage)element).getQualifiedName()); |
| } |
| else if (element instanceof PsiClass) { |
| final String qname = ((PsiClass)element).getQualifiedName(); |
| if (qname != null) { |
| result.add(qname); |
| PsiClass topLevelClass = PsiUtil.getTopLevelClass(element); |
| if (topLevelClass != null) { |
| String topName = topLevelClass.getQualifiedName(); |
| assert topName != null; |
| if (qname.length() > topName.length()) { |
| result.add(topName + qname.substring(topName.length()).replace('.', '$')); |
| } |
| } |
| } |
| } |
| else if (element instanceof PsiMethod) { |
| ContainerUtil.addIfNotNull(result, ((PsiMethod)element).getName()); |
| } |
| else if (element instanceof PsiVariable) { |
| ContainerUtil.addIfNotNull(result, ((PsiVariable)element).getName()); |
| } |
| else if (element instanceof PsiMetaOwner) { |
| final PsiMetaData metaData = ((PsiMetaOwner)element).getMetaData(); |
| if (metaData != null) { |
| ContainerUtil.addIfNotNull(result, metaData.getName()); |
| } |
| } |
| else if (element instanceof PsiNamedElement) { |
| ContainerUtil.addIfNotNull(result, ((PsiNamedElement)element).getName()); |
| } |
| else if (element instanceof XmlAttributeValue) { |
| ContainerUtil.addIfNotNull(result, ((XmlAttributeValue)element).getValue()); |
| } |
| else { |
| LOG.error("Unknown element type: " + element); |
| } |
| } |
| }); |
| |
| return result; |
| } |
| |
| @Override |
| public boolean processElementUsages(@NotNull final PsiElement element, |
| @NotNull final Processor<UsageInfo> processor, |
| @NotNull final FindUsagesOptions options) { |
| if (options instanceof JavaVariableFindUsagesOptions) { |
| final JavaVariableFindUsagesOptions varOptions = (JavaVariableFindUsagesOptions) options; |
| if (varOptions.isReadAccess || varOptions.isWriteAccess){ |
| if (varOptions.isReadAccess && varOptions.isWriteAccess){ |
| if (!addElementUsages(element, options, processor)) return false; |
| } |
| else{ |
| if (!addElementUsages(element, varOptions, new Processor<UsageInfo>() { |
| @Override |
| public boolean process(UsageInfo info) { |
| final PsiElement element = info.getElement(); |
| boolean isWrite = element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element); |
| if (isWrite == varOptions.isWriteAccess) { |
| if (!processor.process(info)) return false; |
| } |
| return true; |
| } |
| })) return false; |
| } |
| } |
| } |
| else if (options.isUsages) { |
| if (!addElementUsages(element, options, processor)) return false; |
| } |
| |
| boolean success = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| if (ThrowSearchUtil.isSearchable (element) && options instanceof JavaThrowFindUsagesOptions && options.isUsages) { |
| ThrowSearchUtil.Root root = ((JavaThrowFindUsagesOptions)options).getRoot(); |
| if (root == null) { |
| final ThrowSearchUtil.Root[] roots = ThrowSearchUtil.getSearchRoots(element); |
| if (roots != null && roots.length > 0) { |
| root = roots [0]; |
| } |
| } |
| if (root != null) { |
| return ThrowSearchUtil.addThrowUsages(processor, root, options); |
| } |
| } |
| return true; |
| } |
| }); |
| if (!success) return false; |
| |
| if (options instanceof JavaPackageFindUsagesOptions && ((JavaPackageFindUsagesOptions)options).isClassesUsages){ |
| if (!addClassesUsages((PsiPackage)element, (JavaPackageFindUsagesOptions)options, processor)) return false; |
| } |
| |
| if (options instanceof JavaClassFindUsagesOptions) { |
| final JavaClassFindUsagesOptions classOptions = (JavaClassFindUsagesOptions)options; |
| final PsiClass psiClass = (PsiClass)element; |
| PsiManager manager = ApplicationManager.getApplication().runReadAction(new Computable<PsiManager>() { |
| @Override |
| public PsiManager compute() { |
| return psiClass.getManager(); |
| } |
| }); |
| if (classOptions.isMethodsUsages){ |
| if (!addMethodsUsages(psiClass, manager, classOptions, processor)) return false; |
| } |
| if (classOptions.isFieldsUsages){ |
| if (!addFieldsUsages(psiClass, manager, classOptions, processor)) return false; |
| } |
| if (psiClass.isInterface()) { |
| if (classOptions.isDerivedInterfaces){ |
| if (classOptions.isImplementingClasses){ |
| if (!addInheritors(psiClass, classOptions, processor)) return false; |
| } |
| else{ |
| if (!addDerivedInterfaces(psiClass, classOptions, processor)) return false; |
| } |
| } |
| else if (classOptions.isImplementingClasses){ |
| if (!addImplementingClasses(psiClass, classOptions, processor)) return false; |
| } |
| |
| if (classOptions.isImplementingClasses) { |
| FunctionalExpressionSearch.search(psiClass, classOptions.searchScope).forEach(new PsiElementProcessorAdapter<PsiFunctionalExpression>( |
| new PsiElementProcessor<PsiFunctionalExpression>() { |
| @Override |
| public boolean execute(@NotNull PsiFunctionalExpression expression) { |
| return addResult(expression, options, processor); |
| } |
| })); |
| } |
| } |
| else if (classOptions.isDerivedClasses) { |
| if (!addInheritors(psiClass, classOptions, processor)) return false; |
| } |
| } |
| |
| if (options instanceof JavaMethodFindUsagesOptions){ |
| final PsiMethod psiMethod = (PsiMethod)element; |
| boolean isAbstract = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| return psiMethod.hasModifierProperty(PsiModifier.ABSTRACT); |
| } |
| }); |
| final JavaMethodFindUsagesOptions methodOptions = (JavaMethodFindUsagesOptions)options; |
| if (isAbstract && methodOptions.isImplementingMethods || methodOptions.isOverridingMethods) { |
| if (!processOverridingMethods(psiMethod, processor, methodOptions)) return false; |
| FunctionalExpressionSearch.search(psiMethod, methodOptions.searchScope).forEach(new PsiElementProcessorAdapter<PsiFunctionalExpression>( |
| new PsiElementProcessor<PsiFunctionalExpression>() { |
| @Override |
| public boolean execute(@NotNull PsiFunctionalExpression expression) { |
| return addResult(expression, options, processor); |
| } |
| })); |
| } |
| } |
| |
| if (element instanceof PomTarget) { |
| if (!addAliasingUsages((PomTarget)element, options, processor)) return false; |
| } |
| final Boolean isSearchable = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| return ThrowSearchUtil.isSearchable(element); |
| } |
| }); |
| if (!isSearchable && options.isSearchForTextOccurrences && options.searchScope instanceof GlobalSearchScope) { |
| // todo add to fastTrack |
| if (!processUsagesInText(element, processor, (GlobalSearchScope)options.searchScope)) return false; |
| } |
| return true; |
| } |
| |
| private static boolean addAliasingUsages(@NotNull PomTarget pomTarget, |
| @NotNull final FindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| for (AliasingPsiTargetMapper aliasingPsiTargetMapper : Extensions.getExtensions(AliasingPsiTargetMapper.EP_NAME)) { |
| for (AliasingPsiTarget psiTarget : aliasingPsiTargetMapper.getTargets(pomTarget)) { |
| boolean success = ReferencesSearch |
| .search(new ReferencesSearch.SearchParameters(PomService.convertToPsi(psiTarget), options.searchScope, false, options.fastTrack)) |
| .forEach(new ReadActionProcessor<PsiReference>() { |
| @Override |
| public boolean processInReadAction(final PsiReference reference) { |
| return addResult(reference, options, processor); |
| } |
| }); |
| if (!success) return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean processOverridingMethods(@NotNull PsiMethod psiMethod, |
| @NotNull final Processor<UsageInfo> processor, |
| @NotNull final JavaMethodFindUsagesOptions options) { |
| return OverridingMethodsSearch.search(psiMethod, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter<PsiMethod>( |
| new PsiElementProcessor<PsiMethod>() { |
| @Override |
| public boolean execute(@NotNull PsiMethod element) { |
| return addResult(element.getNavigationElement(), options, processor); |
| } |
| })); |
| } |
| |
| |
| private static boolean addClassesUsages(@NotNull PsiPackage aPackage, |
| @NotNull final JavaPackageFindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); |
| if (progress != null){ |
| progress.pushState(); |
| } |
| |
| List<PsiClass> classes = new ArrayList<PsiClass>(); |
| addClassesInPackage(aPackage, options.isIncludeSubpackages, classes); |
| for (final PsiClass aClass : classes) { |
| if (progress != null) { |
| String name = ApplicationManager.getApplication().runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return aClass.getName(); |
| } |
| }); |
| progress.setText(FindBundle.message("find.searching.for.references.to.class.progress", name)); |
| progress.checkCanceled(); |
| } |
| boolean success = ReferencesSearch.search(new ReferencesSearch.SearchParameters(aClass, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor<PsiReference>() { |
| @Override |
| public boolean processInReadAction(final PsiReference psiReference) { |
| return addResult(psiReference, options, processor); |
| } |
| }); |
| if (!success) return false; |
| } |
| |
| if (progress != null){ |
| progress.popState(); |
| } |
| return true; |
| } |
| |
| private static void addClassesInPackage(@NotNull final PsiPackage aPackage, boolean includeSubpackages, @NotNull List<PsiClass> array) { |
| PsiDirectory[] dirs = ApplicationManager.getApplication().runReadAction(new Computable<PsiDirectory[]>() { |
| @Override |
| public PsiDirectory[] compute() { |
| return aPackage.getDirectories(); |
| } |
| }); |
| for (PsiDirectory dir : dirs) { |
| addClassesInDirectory(dir, includeSubpackages, array); |
| } |
| } |
| |
| private static void addClassesInDirectory(@NotNull final PsiDirectory dir, |
| final boolean includeSubdirs, |
| @NotNull final List<PsiClass> array) { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| PsiClass[] classes = JavaDirectoryService.getInstance().getClasses(dir); |
| ContainerUtil.addAll(array, classes); |
| if (includeSubdirs) { |
| PsiDirectory[] dirs = dir.getSubdirectories(); |
| for (PsiDirectory directory : dirs) { |
| addClassesInDirectory(directory, includeSubdirs, array); |
| } |
| } |
| } |
| }); |
| } |
| |
| private static boolean addMethodsUsages(@NotNull final PsiClass aClass, |
| @NotNull final PsiManager manager, |
| @NotNull final JavaClassFindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| if (options.isIncludeInherited) { |
| final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(new Computable<PsiMethod[]>() { |
| @Override |
| public PsiMethod[] compute() { |
| return aClass.getAllMethods(); |
| } |
| }); |
| for(int i = 0; i < methods.length; i++){ |
| final PsiMethod method = methods[i]; |
| // filter overridden methods |
| final int finalI = i; |
| final PsiClass methodClass = |
| ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() { |
| @Override |
| public PsiClass compute() { |
| MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY); |
| for (int j = 0; j < finalI; j++) { |
| if (methodSignature.equals(methods[j].getSignature(PsiSubstitutor.EMPTY))) return null; |
| } |
| return method.getContainingClass(); |
| } |
| }); |
| if (methodClass == null) continue; |
| boolean equivalent = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| return manager.areElementsEquivalent(methodClass, aClass); |
| } |
| }); |
| if (equivalent){ |
| if (!addElementUsages(method, options, processor)) return false; |
| } |
| else { |
| MethodReferencesSearch.SearchParameters parameters = |
| new MethodReferencesSearch.SearchParameters(method, options.searchScope, true, options.fastTrack); |
| boolean success = MethodReferencesSearch.search(parameters) |
| .forEach(new PsiReferenceProcessorAdapter(new PsiReferenceProcessor() { |
| @Override |
| public boolean execute(PsiReference reference) { |
| addResultFromReference(reference, methodClass, manager, aClass, options, processor); |
| return true; |
| } |
| })); |
| if (!success) return false; |
| } |
| } |
| } |
| else { |
| PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(new Computable<PsiMethod[]>() { |
| @Override |
| public PsiMethod[] compute() { |
| return aClass.getMethods(); |
| } |
| }); |
| for (PsiMethod method : methods) { |
| if (!addElementUsages(method, options, processor)) return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean addFieldsUsages(@NotNull final PsiClass aClass, |
| @NotNull final PsiManager manager, |
| @NotNull final JavaClassFindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| if (options.isIncludeInherited) { |
| final PsiField[] fields = ApplicationManager.getApplication().runReadAction(new Computable<PsiField[]>() { |
| @Override |
| public PsiField[] compute() { |
| return aClass.getAllFields(); |
| } |
| }); |
| for (int i = 0; i < fields.length; i++) { |
| final PsiField field = fields[i]; |
| // filter hidden fields |
| final int finalI = i; |
| final PsiClass fieldClass = |
| ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() { |
| @Override |
| public PsiClass compute() { |
| for (int j = 0; j < finalI; j++) { |
| if (Comparing.strEqual(field.getName(), fields[j].getName())) return null; |
| } |
| return field.getContainingClass(); |
| } |
| }); |
| if (fieldClass == null) continue; |
| boolean equivalent = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| return manager.areElementsEquivalent(fieldClass, aClass); |
| } |
| }); |
| if (equivalent) { |
| if (!addElementUsages(fields[i], options, processor)) return false; |
| } |
| else { |
| boolean success = ReferencesSearch.search(new ReferencesSearch.SearchParameters(field, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor<PsiReference>() { |
| @Override |
| public boolean processInReadAction(final PsiReference reference) { |
| return addResultFromReference(reference, fieldClass, manager, aClass, options, processor); |
| } |
| }); |
| if (!success) return false; |
| } |
| } |
| } |
| else { |
| PsiField[] fields = ApplicationManager.getApplication().runReadAction(new Computable<PsiField[]>() { |
| @Override |
| public PsiField[] compute() { |
| return aClass.getFields(); |
| } |
| }); |
| for (PsiField field : fields) { |
| if (!addElementUsages(field, options, processor)) return false; |
| } |
| } |
| return true; |
| } |
| |
| @Nullable |
| private static PsiClass getFieldOrMethodAccessedClass(@NotNull PsiReferenceExpression ref, @NotNull PsiClass fieldOrMethodClass) { |
| PsiElement[] children = ref.getChildren(); |
| if (children.length > 1 && children[0] instanceof PsiExpression) { |
| PsiExpression expr = (PsiExpression)children[0]; |
| PsiType type = expr.getType(); |
| if (type != null) { |
| if (!(type instanceof PsiClassType)) return null; |
| return PsiUtil.resolveClassInType(type); |
| } |
| else { |
| if (expr instanceof PsiReferenceExpression) { |
| PsiElement refElement = ((PsiReferenceExpression)expr).resolve(); |
| if (refElement instanceof PsiClass) return (PsiClass)refElement; |
| } |
| return null; |
| } |
| } |
| PsiManager manager = ref.getManager(); |
| for(PsiElement parent = ref; parent != null; parent = parent.getParent()){ |
| if (parent instanceof PsiClass |
| && (manager.areElementsEquivalent(parent, fieldOrMethodClass) || ((PsiClass)parent).isInheritor(fieldOrMethodClass, true))){ |
| return (PsiClass)parent; |
| } |
| } |
| return null; |
| } |
| |
| private static boolean addInheritors(@NotNull PsiClass aClass, |
| @NotNull final JavaClassFindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| return ClassInheritorsSearch.search(aClass, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter<PsiClass>( |
| new PsiElementProcessor<PsiClass>() { |
| @Override |
| public boolean execute(@NotNull PsiClass element) { |
| return addResult(element, options, processor); |
| } |
| |
| })); |
| } |
| |
| private static boolean addDerivedInterfaces(@NotNull PsiClass anInterface, |
| @NotNull final JavaClassFindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| return ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter<PsiClass>( |
| new PsiElementProcessor<PsiClass>() { |
| @Override |
| public boolean execute(@NotNull PsiClass inheritor) { |
| return !inheritor.isInterface() || addResult(inheritor, options, processor); |
| } |
| |
| })); |
| } |
| |
| private static boolean addImplementingClasses(@NotNull PsiClass anInterface, |
| @NotNull final JavaClassFindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| return ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter<PsiClass>( |
| new PsiElementProcessor<PsiClass>() { |
| @Override |
| public boolean execute(@NotNull PsiClass inheritor) { |
| return inheritor.isInterface() || addResult(inheritor, options, processor); |
| } |
| })); |
| } |
| |
| private static boolean addResultFromReference(@NotNull PsiReference reference, |
| @NotNull PsiClass methodClass, |
| @NotNull PsiManager manager, |
| @NotNull PsiClass aClass, |
| @NotNull FindUsagesOptions options, |
| @NotNull Processor<UsageInfo> processor) { |
| PsiElement refElement = reference.getElement(); |
| if (refElement instanceof PsiReferenceExpression) { |
| PsiClass usedClass = getFieldOrMethodAccessedClass((PsiReferenceExpression)refElement, methodClass); |
| if (usedClass != null) { |
| if (manager.areElementsEquivalent(usedClass, aClass) || usedClass.isInheritor(aClass, true)) { |
| if (!addResult(refElement, options, processor)) return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| private static boolean addElementUsages(@NotNull final PsiElement element, |
| @NotNull final FindUsagesOptions options, |
| @NotNull final Processor<UsageInfo> processor) { |
| final SearchScope searchScope = options.searchScope; |
| final PsiClass[] parentClass = new PsiClass[1]; |
| if (element instanceof PsiMethod && ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| PsiMethod method = (PsiMethod)element; |
| parentClass[0] = method.getContainingClass(); |
| return method.isConstructor(); |
| } |
| })) { |
| PsiMethod method = (PsiMethod)element; |
| |
| if (parentClass[0] != null) { |
| boolean strictSignatureSearch = |
| !(options instanceof JavaMethodFindUsagesOptions) || !((JavaMethodFindUsagesOptions)options).isIncludeOverloadUsages; |
| return MethodReferencesSearch |
| .search(new MethodReferencesSearch.SearchParameters(method, searchScope, strictSignatureSearch, options.fastTrack)) |
| .forEach(new ReadActionProcessor<PsiReference>() { |
| @Override |
| public boolean processInReadAction(final PsiReference ref) { |
| return addResult(ref, options, processor); |
| } |
| }); |
| } |
| return true; |
| } |
| |
| final ReadActionProcessor<PsiReference> consumer = new ReadActionProcessor<PsiReference>() { |
| @Override |
| public boolean processInReadAction(final PsiReference ref) { |
| return addResult(ref, options, processor); |
| } |
| }; |
| |
| if (element instanceof PsiMethod) { |
| final boolean strictSignatureSearch = !(options instanceof JavaMethodFindUsagesOptions) || // field with getter |
| !((JavaMethodFindUsagesOptions)options).isIncludeOverloadUsages; |
| return MethodReferencesSearch |
| .search(new MethodReferencesSearch.SearchParameters((PsiMethod)element, searchScope, strictSignatureSearch, options.fastTrack)) |
| .forEach(consumer); |
| } |
| return ReferencesSearch.search(new ReferencesSearch.SearchParameters(element, searchScope, false, options.fastTrack)).forEach(consumer); |
| } |
| |
| private static boolean addResult(@NotNull PsiElement element, |
| @NotNull FindUsagesOptions options, |
| @NotNull Processor<UsageInfo> processor) { |
| return !filterUsage(element, options) || processor.process(new UsageInfo(element)); |
| } |
| |
| private static boolean addResult(@NotNull PsiReference ref, @NotNull FindUsagesOptions options, @NotNull Processor<UsageInfo> processor) { |
| if (filterUsage(ref.getElement(), options)){ |
| TextRange rangeInElement = ref.getRangeInElement(); |
| return processor.process(new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false)); |
| } |
| return true; |
| } |
| |
| private static boolean filterUsage(PsiElement usage, @NotNull FindUsagesOptions options) { |
| if (!(usage instanceof PsiJavaCodeReferenceElement)) { |
| return true; |
| } |
| if (options instanceof JavaPackageFindUsagesOptions && !((JavaPackageFindUsagesOptions)options).isIncludeSubpackages && |
| ((PsiReference)usage).resolve() instanceof PsiPackage) { |
| PsiElement parent = usage.getParent(); |
| if (parent instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)parent).resolve() instanceof PsiPackage) { |
| return false; |
| } |
| } |
| |
| if (!(usage instanceof PsiReferenceExpression)){ |
| if (options instanceof JavaFindUsagesOptions && ((JavaFindUsagesOptions)options).isSkipImportStatements){ |
| PsiElement parent = usage.getParent(); |
| while(parent instanceof PsiJavaCodeReferenceElement){ |
| parent = parent.getParent(); |
| } |
| if (parent instanceof PsiImportStatement){ |
| return false; |
| } |
| } |
| |
| if (options instanceof JavaPackageFindUsagesOptions && ((JavaPackageFindUsagesOptions)options).isSkipPackageStatements){ |
| PsiElement parent = usage.getParent(); |
| while(parent instanceof PsiJavaCodeReferenceElement){ |
| parent = parent.getParent(); |
| } |
| if (parent instanceof PsiPackageStatement){ |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| |
| @Override |
| protected boolean isSearchForTextOccurencesAvailable(@NotNull PsiElement psiElement, boolean isSingleFile) { |
| if (isSingleFile) return false; |
| return new JavaNonCodeSearchElementDescriptionProvider().getElementDescription(psiElement, NonCodeSearchDescriptionLocation.NON_JAVA) != null; |
| |
| } |
| |
| @NotNull |
| @Override |
| public Collection<PsiReference> findReferencesToHighlight(@NotNull final PsiElement target, @NotNull final SearchScope searchScope) { |
| if (target instanceof PsiMethod) { |
| final PsiMethod[] superMethods = ((PsiMethod)target).findDeepestSuperMethods(); |
| if (superMethods.length == 0) { |
| return MethodReferencesSearch.search((PsiMethod)target, searchScope, true).findAll(); |
| } |
| final Collection<PsiReference> result = new ArrayList<PsiReference>(); |
| for (PsiMethod superMethod : superMethods) { |
| result.addAll(MethodReferencesSearch.search(superMethod, searchScope, true).findAll()); |
| } |
| return result; |
| } |
| return super.findReferencesToHighlight(target, searchScope); |
| } |
| } |