| /* |
| * 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.refactoring.turnRefsToSuper; |
| |
| import com.intellij.internal.diGraph.analyzer.GlobalAnalyzer; |
| import com.intellij.internal.diGraph.analyzer.Mark; |
| import com.intellij.internal.diGraph.analyzer.MarkedNode; |
| import com.intellij.internal.diGraph.analyzer.OneEndFunctor; |
| import com.intellij.internal.diGraph.impl.EdgeImpl; |
| import com.intellij.internal.diGraph.impl.NodeImpl; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.psi.*; |
| import com.intellij.psi.javadoc.PsiDocComment; |
| import com.intellij.psi.search.LocalSearchScope; |
| import com.intellij.psi.search.PsiSearchHelper; |
| import com.intellij.psi.search.searches.ClassInheritorsSearch; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.util.*; |
| import com.intellij.refactoring.BaseRefactoringProcessor; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.rename.AutomaticRenamingDialog; |
| import com.intellij.refactoring.rename.naming.AutomaticVariableRenamer; |
| import com.intellij.refactoring.util.MoveRenameUsageInfo; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.containers.HashSet; |
| import com.intellij.util.containers.Queue; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * @author dsl |
| */ |
| public abstract class TurnRefsToSuperProcessorBase extends BaseRefactoringProcessor { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.turnRefsToSuper.TurnRefsToSuperProcessorBase"); |
| protected PsiClass myClass; |
| protected final boolean myReplaceInstanceOf; |
| protected PsiManager myManager; |
| protected PsiSearchHelper mySearchHelper; |
| protected HashSet<PsiElement> myMarkedNodes = new HashSet<PsiElement>(); |
| private Queue<PsiExpression> myExpressionsQueue; |
| protected HashMap<PsiElement, Node> myElementToNode = new HashMap<PsiElement, Node>(); |
| protected Map<SmartPsiElementPointer, String> myVariablesRenames = new HashMap<SmartPsiElementPointer, String>(); |
| private final String mySuperClassName; |
| private final List<UsageInfo> myVariablesUsages = new ArrayList<UsageInfo>(); |
| |
| @Override |
| protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) { |
| UsageInfo[] usages = refUsages.get(); |
| List<UsageInfo> filtered = new ArrayList<UsageInfo>(); |
| for (UsageInfo usage : usages) { |
| if (usage instanceof TurnToSuperReferenceUsageInfo) { |
| filtered.add(usage); |
| } |
| } |
| |
| myVariableRenamer = new AutomaticVariableRenamer(myClass, mySuperClassName, filtered); |
| if (!ApplicationManager.getApplication().isUnitTestMode() && |
| myVariableRenamer.hasAnythingToRename()) { |
| final AutomaticRenamingDialog dialog = new AutomaticRenamingDialog(myProject, myVariableRenamer); |
| dialog.show(); |
| if (!dialog.isOK()) return false; |
| |
| final List<PsiNamedElement> variables = myVariableRenamer.getElements(); |
| for (final PsiNamedElement namedElement : variables) { |
| final PsiVariable variable = (PsiVariable)namedElement; |
| final SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(variable); |
| myVariablesRenames.put(pointer, myVariableRenamer.getNewName(variable)); |
| } |
| |
| Runnable runnable = new Runnable() { |
| @Override |
| public void run() { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| myVariableRenamer.findUsages(myVariablesUsages, false, false); |
| } |
| }); |
| } |
| }; |
| |
| if (!ProgressManager.getInstance() |
| .runProcessWithProgressSynchronously(runnable, RefactoringBundle.message("searching.for.variables"), true, myProject)) { |
| return false; |
| } |
| } |
| |
| prepareSuccessful(); |
| return true; |
| } |
| |
| private AutomaticVariableRenamer myVariableRenamer; |
| |
| protected void performVariablesRenaming() { |
| try { |
| //forget about smart pointers |
| Map<PsiElement, String> variableRenames = new HashMap<PsiElement, String>(); |
| for (Map.Entry<SmartPsiElementPointer, String> entry : myVariablesRenames.entrySet()) { |
| variableRenames.put(entry.getKey().getElement(), entry.getValue()); |
| } |
| |
| for (UsageInfo usage : myVariablesUsages) { |
| if (usage instanceof MoveRenameUsageInfo) { |
| final MoveRenameUsageInfo renameUsageInfo = ((MoveRenameUsageInfo)usage); |
| final String newName = variableRenames.get(renameUsageInfo.getUpToDateReferencedElement()); |
| final PsiReference reference = renameUsageInfo.getReference(); |
| if (reference != null) { |
| reference.handleElementRename(newName); |
| } |
| } |
| } |
| |
| for (Map.Entry<SmartPsiElementPointer,String> entry : myVariablesRenames.entrySet()) { |
| final String newName = entry.getValue(); |
| if (newName != null) { |
| final PsiVariable variable = (PsiVariable)entry.getKey().getElement(); |
| variable.setName(newName); |
| } |
| } |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| |
| protected TurnRefsToSuperProcessorBase(Project project, boolean replaceInstanceOf, String superClassName) { |
| super(project); |
| mySuperClassName = superClassName; |
| myManager = PsiManager.getInstance(project); |
| mySearchHelper = PsiSearchHelper.SERVICE.getInstance(myManager.getProject()); |
| myManager = PsiManager.getInstance(myProject); |
| myReplaceInstanceOf = replaceInstanceOf; |
| } |
| |
| protected ArrayList<UsageInfo> detectTurnToSuperRefs(PsiReference[] refs, final ArrayList<UsageInfo> result) { |
| buildGraph(refs); |
| |
| for (PsiReference ref : refs) { |
| final PsiElement element = ref.getElement(); |
| if (canTurnToSuper(element)) { |
| result.add(new TurnToSuperReferenceUsageInfo(element)); |
| } |
| } |
| return result; |
| } |
| |
| protected boolean canTurnToSuper(PsiElement ref) { |
| return !myMarkedNodes.contains(ref); |
| } |
| |
| protected static void processTurnToSuperRefs(UsageInfo[] usages, final PsiClass aSuper) throws IncorrectOperationException { |
| for (UsageInfo usage : usages) { |
| if (usage instanceof TurnToSuperReferenceUsageInfo) { |
| final PsiElement element = usage.getElement(); |
| if (element != null) { |
| final PsiReference ref = element.getReference(); |
| assert ref != null; |
| PsiElement newElement = ref.bindToElement(aSuper); |
| |
| if (newElement.getParent() instanceof PsiTypeElement) { |
| if (newElement.getParent().getParent() instanceof PsiTypeCastExpression) { |
| fixPossiblyRedundantCast((PsiTypeCastExpression)newElement.getParent().getParent()); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private static void fixPossiblyRedundantCast(PsiTypeCastExpression cast) throws IncorrectOperationException { |
| PsiTypeElement castTypeElement = cast.getCastType(); |
| if (castTypeElement == null) return; |
| PsiClass castClass = PsiUtil.resolveClassInType(castTypeElement.getType()); |
| if (castClass == null) return; |
| |
| PsiExpression operand = cast.getOperand(); |
| if (operand == null) return; |
| PsiClass operandClass = PsiUtil.resolveClassInType(RefactoringUtil.getTypeByExpression(operand)); |
| if (operandClass == null) return; |
| |
| if (!castClass.getManager().areElementsEquivalent(castClass, operandClass) && |
| !operandClass.isInheritor(castClass, true)) { |
| return; |
| } |
| // OK, cast is redundant |
| PsiExpression exprToReplace = cast; |
| while (exprToReplace.getParent() instanceof PsiParenthesizedExpression) { |
| exprToReplace = (PsiExpression)exprToReplace.getParent(); |
| } |
| exprToReplace.replace(operand); |
| } |
| |
| private void buildGraph(PsiReference[] refs) { |
| myMarkedNodes.clear(); |
| myExpressionsQueue = new Queue<PsiExpression>(refs.length); |
| myElementToNode.clear(); |
| for (PsiReference ref : refs) { |
| processUsage(ref.getElement()); |
| } |
| |
| processQueue(); |
| |
| markNodes(); |
| |
| spreadMarks(); |
| } |
| |
| private void processUsage(PsiElement ref) { |
| if (ref instanceof PsiReferenceExpression) { |
| final PsiElement parent = ref.getParent(); |
| if (parent instanceof PsiReferenceExpression) { |
| final PsiReferenceExpression refExpr = (PsiReferenceExpression)parent; |
| final PsiElement refMember = refExpr.resolve(); |
| if (!isInSuper(refMember)) { |
| markNode(ref); |
| } |
| } |
| return; |
| } |
| |
| PsiElement parent = ref.getParent(); |
| if (parent instanceof PsiTypeElement) { |
| PsiElement pparent = parent.getParent(); |
| while (pparent instanceof PsiTypeElement) { |
| addLink(pparent, parent); |
| addLink(parent, pparent); |
| parent = pparent; |
| pparent = parent.getParent(); |
| } |
| final PsiTypeElement typeElement = (PsiTypeElement)parent; |
| |
| addLink(typeElement, ref); |
| addLink(ref, typeElement); |
| |
| if (pparent instanceof PsiVariable) { |
| processVariableType((PsiVariable)pparent); |
| } |
| else if (pparent instanceof PsiMethod) { |
| processMethodReturnType((PsiMethod)pparent); |
| } |
| else if (pparent instanceof PsiTypeCastExpression) { |
| addLink(pparent, typeElement); |
| addLink(typeElement, pparent); |
| } |
| else if (pparent instanceof PsiReferenceParameterList) { |
| final PsiReferenceParameterList refParameterList = ((PsiReferenceParameterList)pparent); |
| final PsiElement ppparent = pparent.getParent(); |
| if (ppparent instanceof PsiJavaCodeReferenceElement) { |
| final PsiJavaCodeReferenceElement classReference = (PsiJavaCodeReferenceElement)ppparent; |
| if (classReference.getParent() instanceof PsiReferenceList) { |
| final PsiReferenceList referenceList = ((PsiReferenceList)ppparent.getParent()); |
| final PsiClass parentClass = PsiTreeUtil.getParentOfType(ref, PsiClass.class); |
| if (parentClass != null) { |
| if (referenceList.equals(parentClass.getExtendsList()) || referenceList.equals(parentClass.getImplementsList())) { |
| final PsiTypeElement[] typeParameterElements = refParameterList.getTypeParameterElements(); |
| for (int i = 0; i < typeParameterElements.length; i++) { |
| if (typeParameterElements[i] == typeElement) { |
| final PsiElement resolved = classReference.resolve(); |
| if (resolved instanceof PsiClass) { |
| final PsiTypeParameter[] typeParameters = ((PsiClass)resolved).getTypeParameters(); |
| if (typeParameters.length > i) { |
| linkTypeParameterInstantiations(typeParameters[i], typeElement, parentClass); |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| } else if (classReference.getParent() instanceof PsiTypeElement) { |
| processUsage(classReference); |
| return; |
| } else if (classReference.getParent() instanceof PsiNewExpression) { |
| final PsiVariable variable = PsiTreeUtil.getParentOfType(classReference, PsiVariable.class); |
| if (variable != null) { |
| processUsage(variable); |
| return; |
| } |
| } else if (classReference.getParent() instanceof PsiAnonymousClass) { |
| processUsage(classReference); |
| return; |
| } |
| } |
| markNode(ref); //??? |
| } |
| } |
| else if (parent instanceof PsiNewExpression) { |
| PsiNewExpression newExpression = (PsiNewExpression)parent; |
| if (newExpression.getType() instanceof PsiArrayType) { |
| addLink(newExpression, ref); |
| addLink(ref, newExpression); |
| PsiArrayInitializerExpression initializer = newExpression.getArrayInitializer(); |
| if (initializer != null) { |
| addLink(ref, initializer); |
| } |
| checkToArray(ref, newExpression); |
| } |
| else { |
| markNode(ref); |
| } |
| } |
| else if (parent instanceof PsiJavaCodeReferenceElement && ref.equals(((PsiJavaCodeReferenceElement)parent).getQualifier())) { |
| final PsiElement resolved = ((PsiJavaCodeReferenceElement)parent).resolve(); |
| if (resolved == null || !isInSuper(resolved)) { |
| markNode(ref); |
| } |
| } |
| else { |
| markNode(ref); |
| } |
| } |
| |
| private void linkTypeParameterInstantiations (PsiTypeParameter typeParameter, final PsiTypeElement instantiation, final PsiClass inheritingClass) { |
| final PsiTypeParameterListOwner owner = typeParameter.getOwner(); |
| if (owner instanceof PsiClass) { |
| final PsiClass ownerClass = ((PsiClass)owner); |
| final LocalSearchScope derivedScope = new LocalSearchScope(inheritingClass); |
| final PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(ownerClass, inheritingClass, PsiSubstitutor.EMPTY); |
| if (substitutor == null) return; |
| final LocalSearchScope baseScope = new LocalSearchScope(ownerClass); |
| ReferencesSearch.search(typeParameter, baseScope).forEach(new Processor<PsiReference>() { |
| @Override |
| public boolean process(final PsiReference ref) { |
| final PsiElement element = ref.getElement(); |
| final PsiElement parent = element.getParent(); |
| if (parent instanceof PsiTypeElement) { |
| final PsiElement pparent = parent.getParent(); |
| if (pparent instanceof PsiMethod && parent.equals(((PsiMethod)pparent).getReturnTypeElement())) { |
| final PsiMethod method = (PsiMethod)pparent; |
| final MethodSignature signature = method.getSignature(substitutor); |
| if (PsiUtil.isAccessible(method, inheritingClass, null)) { |
| final PsiMethod inInheritor = MethodSignatureUtil.findMethodBySignature(inheritingClass, signature, false); |
| if (inInheritor != null && inInheritor.getReturnTypeElement() != null) { |
| addLink(instantiation, method.getReturnTypeElement()); |
| addLink(method.getReturnTypeElement(), instantiation); |
| } |
| } |
| } else if (pparent instanceof PsiParameter) { |
| final PsiParameter parameter = (PsiParameter)pparent; |
| if (parameter.getDeclarationScope() instanceof PsiMethod) { |
| PsiMethod method = (PsiMethod)parameter.getDeclarationScope(); |
| final int index = ((PsiParameterList)parameter.getParent()).getParameterIndex(parameter); |
| final MethodSignature signature = method.getSignature(substitutor); |
| if (PsiUtil.isAccessible(method, inheritingClass, null)) { |
| final PsiMethod inInheritor = MethodSignatureUtil.findMethodBySignature(inheritingClass, signature, false); |
| if (inInheritor != null) { |
| final PsiParameter[] inheritorParams = inInheritor.getParameterList().getParameters(); |
| LOG.assertTrue(inheritorParams.length > index); |
| final PsiTypeElement hisTypeElement = inheritorParams[index].getTypeElement(); |
| addLink(instantiation, hisTypeElement); |
| addLink(hisTypeElement, instantiation); |
| } |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| }); |
| } |
| } |
| |
| private void addArgumentParameterLink(PsiElement arg, PsiExpressionList actualArgsList, PsiMethod method) { |
| PsiParameter[] params = method.getParameterList().getParameters(); |
| PsiExpression[] actualArgs = actualArgsList.getExpressions(); |
| int argIndex = -1; |
| for (int i = 0; i < actualArgs.length; i++) { |
| PsiExpression actualArg = actualArgs[i]; |
| if (actualArg.equals(arg)) { |
| argIndex = i; |
| break; |
| } |
| } |
| |
| if (argIndex >= 0 && argIndex < params.length) { |
| addLink(params[argIndex], arg); |
| } |
| else if (method.isVarArgs() && argIndex >= params.length) { |
| addLink(params[params.length - 1], arg); |
| } |
| } |
| |
| private void checkToArray(PsiElement ref, PsiNewExpression newExpression) { |
| PsiElement tmp; |
| |
| final PsiClass javaUtilCollectionClass = |
| JavaPsiFacade.getInstance(myManager.getProject()).findClass("java.util.Collection", ref.getResolveScope()); |
| if (javaUtilCollectionClass == null) return; |
| tmp = newExpression.getParent(); |
| if (!(tmp instanceof PsiExpressionList)) return; |
| tmp = tmp.getParent(); |
| if (!(tmp instanceof PsiMethodCallExpression)) return; |
| PsiMethodCallExpression methodCall = (PsiMethodCallExpression)tmp; |
| tmp = tmp.getParent(); |
| if (!(tmp instanceof PsiTypeCastExpression)) return; |
| PsiTypeCastExpression typeCast = (PsiTypeCastExpression)tmp; |
| |
| PsiReferenceExpression methodRef = methodCall.getMethodExpression(); |
| tmp = methodRef.resolve(); |
| if (!(tmp instanceof PsiMethod)) return; |
| PsiMethod method = (PsiMethod)tmp; |
| @NonNls final String name = method.getName(); |
| if (!name.equals("toArray")) return; |
| |
| PsiClass methodClass = method.getContainingClass(); |
| if (!methodClass.isInheritor(javaUtilCollectionClass, true)) return; |
| |
| // ok, this is an implementation of java.util.Collection.toArray |
| addLink(typeCast, ref); |
| |
| } |
| |
| private void processVariableType(PsiVariable variable) { |
| final PsiTypeElement type = variable.getTypeElement(); |
| final PsiExpression initializer = variable.getInitializer(); |
| if (initializer != null) { |
| addLink(type, initializer); |
| } |
| |
| for (PsiReference ref : ReferencesSearch.search(variable)) { |
| final PsiElement element = ref.getElement(); |
| addLink(element, type); |
| addLink(type, element); |
| analyzeVarUsage(element); |
| } |
| |
| if (variable instanceof PsiParameter) { |
| final PsiElement declScope = ((PsiParameter)variable).getDeclarationScope(); |
| if (declScope instanceof PsiCatchSection) { |
| markNode(type); |
| } |
| else if (declScope instanceof PsiForeachStatement) { |
| final PsiExpression iteratedValue = ((PsiForeachStatement)declScope).getIteratedValue(); |
| addLink(type, iteratedValue); |
| } |
| else if (declScope instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)declScope; |
| final int index = method.getParameterList().getParameterIndex((PsiParameter)variable); |
| |
| { |
| for (PsiReference call : ReferencesSearch.search(method)) { |
| PsiElement ref = call.getElement(); |
| PsiExpressionList argumentList; |
| if (ref.getParent() instanceof PsiCall) { |
| argumentList = ((PsiCall)ref.getParent()).getArgumentList(); |
| } |
| else if (ref.getParent() instanceof PsiAnonymousClass) { |
| argumentList = ((PsiConstructorCall)ref.getParent().getParent()).getArgumentList(); |
| } |
| else { |
| continue; |
| } |
| if (argumentList == null) continue; |
| PsiExpression[] args = argumentList.getExpressions(); |
| if (index >= args.length) continue; |
| addLink(type, args[index]); |
| } |
| } |
| |
| final class Inner { |
| void linkInheritors(final PsiMethod[] methods) { |
| for (final PsiMethod superMethod : methods) { |
| final PsiParameter[] parameters = superMethod.getParameterList().getParameters(); |
| if (index >= parameters.length) continue; |
| final PsiTypeElement superType = parameters[index].getTypeElement(); |
| addLink(superType, type); |
| addLink(type, superType); |
| } |
| } |
| } |
| |
| final PsiMethod[] superMethods = method.findSuperMethods(); |
| new Inner().linkInheritors(superMethods); |
| PsiClass containingClass = method.getContainingClass(); |
| List<PsiClass> subClasses = new ArrayList<PsiClass>(ClassInheritorsSearch.search(containingClass, false).findAll()); |
| // ??? In the theory this is non-efficient way: too many inheritors can be processed. |
| // ??? But in real use it seems reasonably fast. If poor performance problems emerged, |
| // ??? should be optimized |
| for (int i1 = 0; i1 != subClasses.size(); ++i1) { |
| final PsiMethod[] mBSs = subClasses.get(i1).findMethodsBySignature(method, true); |
| new Inner().linkInheritors(mBSs); |
| } |
| } |
| else { |
| LOG.error("Unexpected scope: " + declScope); |
| } |
| } |
| else if (variable instanceof PsiResourceVariable) { |
| final PsiJavaParserFacade facade = JavaPsiFacade.getInstance(myProject).getParserFacade(); |
| checkConstrainingType(type, facade.createTypeFromText(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, variable)); |
| } |
| } |
| |
| private void analyzeVarUsage(final PsiElement element) { |
| PsiType constrainingType = null; |
| |
| final PsiElement parent = element.getParent(); |
| if (parent instanceof PsiReturnStatement) { |
| final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); |
| assert method != null; |
| constrainingType = method.getReturnType(); |
| } |
| else if (parent instanceof PsiAssignmentExpression) { |
| constrainingType = ((PsiAssignmentExpression)parent).getLExpression().getType(); |
| } |
| //todo[ann] this works for AImpl->A but fails on List<AImpl> (see testForEach1() and testIDEADEV23807()). |
| //else if (parent instanceof PsiForeachStatement) { |
| // final PsiType exprType = ((PsiExpression)element).getType(); |
| // if (!(exprType instanceof PsiArrayType)) { |
| // final PsiJavaParserFacade facade = JavaPsiFacade.getInstance(myProject).getParserFacade(); |
| // constrainingType = facade.createTypeFromText(CommonClassNames.JAVA_LANG_ITERABLE, parent); |
| // } |
| //} |
| else if (parent instanceof PsiLocalVariable) { |
| constrainingType = ((PsiLocalVariable)parent).getType(); |
| } |
| |
| checkConstrainingType(element, constrainingType); |
| } |
| |
| private void checkConstrainingType(PsiElement element, @Nullable PsiType constrainingType) { |
| if (constrainingType instanceof PsiClassType) { |
| final PsiClass resolved = ((PsiClassType)constrainingType).resolve(); |
| if (!myClass.equals(resolved)) { |
| if (resolved == null || !isSuperInheritor(resolved)) { |
| markNode(element); |
| } |
| } |
| } |
| } |
| |
| private void processMethodReturnType(final PsiMethod method) { |
| final PsiTypeElement returnType = method.getReturnTypeElement(); |
| for (PsiReference call : ReferencesSearch.search(method)) { |
| final PsiElement ref = call.getElement(); |
| if (PsiTreeUtil.getParentOfType(ref, PsiDocComment.class) != null) continue; |
| final PsiElement parent = ref.getParent(); |
| addLink(parent, returnType); |
| } |
| |
| final PsiReturnStatement[] returnStatements = PsiUtil.findReturnStatements(method); |
| for (final PsiReturnStatement returnStatement : returnStatements) { |
| final PsiExpression returnValue = returnStatement.getReturnValue(); |
| if (returnValue != null) { |
| addLink(returnType, returnValue); |
| } |
| } |
| |
| final PsiMethod[] superMethods = method.findSuperMethods(); |
| final class Inner { |
| public void linkInheritors(final PsiMethod[] methods) { |
| for (final PsiMethod superMethod : methods) { |
| final PsiTypeElement superType = superMethod.getReturnTypeElement(); |
| addLink(superType, returnType); |
| addLink(returnType, superType); |
| } |
| } |
| } |
| |
| new Inner().linkInheritors(superMethods); |
| // ??? In the theory this is non-efficient way: too many inheritors can be processed (and multiple times). |
| // ??? But in real use it seems reasonably fast. If poor performance problems emerged, |
| // ??? should be optimized |
| PsiClass containingClass = method.getContainingClass(); |
| final PsiClass[] subClasses = ClassInheritorsSearch.search(containingClass, false).toArray(PsiClass.EMPTY_ARRAY); |
| for (int i1 = 0; i1 != subClasses.length; ++i1) { |
| final PsiMethod[] mBSs = subClasses[i1].findMethodsBySignature(method, true); |
| new Inner().linkInheritors(mBSs); |
| } |
| } |
| |
| private void processQueue() { |
| while (!myExpressionsQueue.isEmpty()) { |
| PsiExpression expr = myExpressionsQueue.pullFirst(); |
| PsiElement parent = expr.getParent(); |
| if (parent instanceof PsiAssignmentExpression) { |
| PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent; |
| if (assignment.getRExpression() != null) { |
| addLink(assignment.getLExpression(), assignment.getRExpression()); |
| } |
| addLink(assignment, assignment.getLExpression()); |
| addLink(assignment.getLExpression(), assignment); |
| } |
| else if (parent instanceof PsiArrayAccessExpression) { |
| PsiArrayAccessExpression arrayAccess = (PsiArrayAccessExpression)parent; |
| if (expr.equals(arrayAccess.getArrayExpression())) { |
| addLink(arrayAccess, expr); |
| addLink(expr, arrayAccess); |
| } |
| } |
| else if (parent instanceof PsiParenthesizedExpression) { |
| addLink(parent, expr); |
| addLink(expr, parent); |
| } |
| else if (parent instanceof PsiArrayInitializerExpression) { |
| PsiArrayInitializerExpression arrayInitializerExpr = (PsiArrayInitializerExpression)parent; |
| PsiExpression[] initializers = arrayInitializerExpr.getInitializers(); |
| for (PsiExpression initializer : initializers) { |
| addLink(arrayInitializerExpr, initializer); |
| } |
| } |
| else if (parent instanceof PsiExpressionList) { |
| PsiElement pparent = parent.getParent(); |
| if (pparent instanceof PsiCallExpression) { |
| PsiMethod method = ((PsiCallExpression)pparent).resolveMethod(); |
| if (method != null) { |
| addArgumentParameterLink(expr, (PsiExpressionList)parent, method); |
| } |
| } |
| } |
| } |
| } |
| |
| protected void markNodes() { |
| //for (Iterator iterator = myDependencyMap.keySet().getSectionsIterator(); getSectionsIterator.hasNext();) { |
| for (final PsiElement element : myElementToNode.keySet()) { |
| if (element instanceof PsiExpression) { |
| final PsiElement parent = element.getParent(); |
| if (parent instanceof PsiReferenceExpression) { |
| final PsiReferenceExpression refExpr = (PsiReferenceExpression)parent; |
| if (element.equals(refExpr.getQualifierExpression())) { |
| final PsiElement refElement = refExpr.resolve(); |
| if (refElement != null && !isInSuper(refElement)) { |
| markNode(element); |
| } |
| } |
| } |
| } |
| else if (!myReplaceInstanceOf && element.getParent() != null |
| && element.getParent().getParent() instanceof PsiInstanceOfExpression) { |
| markNode(element); |
| } |
| else if (element.getParent() instanceof PsiClassObjectAccessExpression) { |
| markNode(element); |
| } |
| else if (element instanceof PsiParameter) { |
| final PsiType type = TypeConversionUtil.erasure(((PsiParameter)element).getType()); |
| final PsiClass aClass = PsiUtil.resolveClassInType(type); |
| if (aClass != null) { |
| if (!myManager.isInProject(element) || !myManager.areElementsEquivalent(aClass, myClass)) { |
| if (!isSuperInheritor(aClass)) { |
| markNode(element); |
| } |
| } |
| } |
| else { // unresolvable class |
| markNode(element); |
| } |
| } |
| } |
| } |
| |
| protected abstract boolean isSuperInheritor(PsiClass aClass); |
| |
| protected abstract boolean isInSuper(PsiElement member); |
| |
| protected void addLink(PsiElement source, PsiElement target) { |
| Node from = myElementToNode.get(source); |
| Node to = myElementToNode.get(target); |
| |
| if (from == null) { |
| from = new Node(source); |
| if (source instanceof PsiExpression) myExpressionsQueue.addLast((PsiExpression)source); |
| myElementToNode.put(source, from); |
| } |
| |
| if (to == null) { |
| to = new Node(target); |
| if (target instanceof PsiExpression) myExpressionsQueue.addLast((PsiExpression)target); |
| myElementToNode.put(target, to); |
| } |
| |
| Edge.connect(from, to); |
| } |
| |
| private void spreadMarks() { |
| final LinkedList<MarkedNode> markedNodes = new LinkedList<MarkedNode>(); |
| |
| for (final PsiElement markedNode : myMarkedNodes) { |
| final Node node = myElementToNode.get(markedNode); |
| if (node != null) markedNodes.addFirst(node); |
| } |
| |
| GlobalAnalyzer.doOneEnd(markedNodes, new Colorer()); |
| } |
| |
| private void markNode(final PsiElement node) { |
| myMarkedNodes.add(node); |
| } |
| |
| class Colorer implements OneEndFunctor { |
| @Override |
| public Mark compute(Mark from, Mark edge, Mark to) { |
| VisitMark mark = new VisitMark((VisitMark)to); |
| |
| myMarkedNodes.add(mark.getElement()); |
| mark.switchOn(); |
| |
| return mark; |
| } |
| } |
| |
| private static class Edge extends EdgeImpl { |
| private Edge(Node from, Node to) { |
| super(from, to); |
| } |
| |
| public static boolean connect(Node from, Node to) { |
| if (from.mySuccessors.add(to)) { |
| new Edge(from, to); |
| return true; |
| } |
| |
| return false; |
| } |
| } |
| |
| private static class VisitMark implements Mark { |
| private boolean myVisited; |
| private final PsiElement myElement; |
| |
| @Override |
| public boolean coincidesWith(Mark x) { |
| return ((VisitMark)x).myVisited == myVisited; |
| } |
| |
| public VisitMark(VisitMark m) { |
| myVisited = false; |
| myElement = m.myElement; |
| } |
| |
| public VisitMark(PsiElement e) { |
| myVisited = false; |
| myElement = e; |
| } |
| |
| public void switchOn() { |
| myVisited = true; |
| } |
| |
| public void switchOff() { |
| myVisited = false; |
| } |
| |
| public PsiElement getElement() { |
| return myElement; |
| } |
| } |
| |
| private static class Node extends NodeImpl { |
| private final HashSet<Node> mySuccessors = new HashSet<Node>(); |
| private VisitMark myMark; |
| |
| public Node(PsiElement x) { |
| super(); |
| myMark = new VisitMark(x); |
| } |
| |
| @Override |
| public Mark getMark() { |
| return myMark; |
| } |
| |
| @Override |
| public void setMark(Mark x) { |
| myMark = (VisitMark)x; |
| } |
| } |
| } |