| /* |
| * Copyright 2000-2013 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.slicer; |
| |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.searches.MethodReferencesSearch; |
| import com.intellij.psi.search.searches.OverridingMethodsSearch; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.util.MethodSignatureUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.ArrayUtilRt; |
| import com.intellij.util.Processor; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| /** |
| * @author cdr |
| */ |
| public class SliceForwardUtil { |
| public static boolean processUsagesFlownFromThe(@NotNull PsiElement element, @NotNull final Processor<SliceUsage> processor, @NotNull final SliceUsage parent) { |
| Pair<PsiElement, PsiSubstitutor> pair = getAssignmentTarget(element, parent); |
| if (pair != null) { |
| PsiElement target = pair.getFirst(); |
| final PsiSubstitutor substitutor = pair.getSecond(); |
| if (target instanceof PsiParameter) { |
| PsiParameter parameter = (PsiParameter)target; |
| PsiElement declarationScope = parameter.getDeclarationScope(); |
| if (declarationScope instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)declarationScope; |
| final int parameterIndex = method.getParameterList().getParameterIndex(parameter); |
| |
| Processor<PsiMethod> myProcessor = new Processor<PsiMethod>() { |
| @Override |
| public boolean process(PsiMethod override) { |
| if (!parent.getScope().contains(override)) return true; |
| final PsiSubstitutor superSubstitutor = method == override |
| ? substitutor |
| : MethodSignatureUtil.getSuperMethodSignatureSubstitutor(method.getSignature(substitutor), |
| override.getSignature(substitutor)); |
| |
| PsiParameter[] parameters = override.getParameterList().getParameters(); |
| if (parameters.length <= parameterIndex) return true; |
| PsiParameter actualParam = parameters[parameterIndex]; |
| |
| SliceUsage usage = SliceUtil.createSliceUsage(actualParam, parent, superSubstitutor,parent.indexNesting, ""); |
| return processor.process(usage); |
| } |
| }; |
| if (!myProcessor.process(method)) return false; |
| return OverridingMethodsSearch.search(method, parent.getScope().toSearchScope(), true).forEach(myProcessor); |
| } |
| } |
| |
| SliceUsage usage = SliceUtil.createSliceUsage(target, parent, parent.getSubstitutor(),parent.indexNesting, ""); |
| return processor.process(usage); |
| } |
| |
| if (element instanceof PsiReferenceExpression) { |
| PsiReferenceExpression ref = (PsiReferenceExpression)element; |
| PsiElement resolved = ref.resolve(); |
| if (!(resolved instanceof PsiVariable)) return true; |
| final PsiVariable variable = (PsiVariable)resolved; |
| return processAssignedFrom(variable, ref, parent, processor); |
| } |
| if (element instanceof PsiVariable) { |
| return processAssignedFrom(element, element, parent, processor); |
| } |
| if (element instanceof PsiMethod) { |
| return processAssignedFrom(element, element, parent, processor); |
| } |
| return true; |
| } |
| |
| private static boolean processAssignedFrom(final PsiElement from, |
| final PsiElement context, |
| final SliceUsage parent, |
| @NotNull final Processor<SliceUsage> processor) { |
| if (from instanceof PsiLocalVariable) { |
| return searchReferencesAndProcessAssignmentTarget(from, context, parent, processor); |
| } |
| if (from instanceof PsiParameter) { |
| PsiParameter parameter = (PsiParameter)from; |
| PsiElement scope = parameter.getDeclarationScope(); |
| Collection<PsiParameter> parametersToAnalyze = new THashSet<PsiParameter>(); |
| if (scope instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)scope; |
| int index = method.getParameterList().getParameterIndex(parameter); |
| |
| Collection<PsiMethod> superMethods = new THashSet<PsiMethod>(Arrays.asList(method.findDeepestSuperMethods())); |
| superMethods.add(method); |
| for (Iterator<PsiMethod> iterator = superMethods.iterator(); iterator.hasNext(); ) { |
| ProgressManager.checkCanceled(); |
| PsiMethod superMethod = iterator.next(); |
| if (!parent.params.scope.contains(superMethod)) { |
| iterator.remove(); |
| } |
| } |
| |
| final THashSet<PsiMethod> implementors = new THashSet<PsiMethod>(superMethods); |
| for (PsiMethod superMethod : superMethods) { |
| ProgressManager.checkCanceled(); |
| if (!OverridingMethodsSearch.search(superMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiMethod>() { |
| @Override |
| public boolean process(PsiMethod sub) { |
| ProgressManager.checkCanceled(); |
| implementors.add(sub); |
| return true; |
| } |
| })) return false; |
| } |
| for (PsiMethod implementor : implementors) { |
| ProgressManager.checkCanceled(); |
| if (!parent.params.scope.contains(implementor)) continue; |
| if (implementor instanceof PsiCompiledElement) implementor = (PsiMethod)implementor.getNavigationElement(); |
| |
| PsiParameter[] parameters = implementor.getParameterList().getParameters(); |
| if (index != -1 && index < parameters.length) { |
| parametersToAnalyze.add(parameters[index]); |
| } |
| } |
| } |
| else { |
| parametersToAnalyze.add(parameter); |
| } |
| for (final PsiParameter psiParameter : parametersToAnalyze) { |
| ProgressManager.checkCanceled(); |
| |
| if (!searchReferencesAndProcessAssignmentTarget(psiParameter, null, parent, processor)) return false; |
| } |
| return true; |
| } |
| if (from instanceof PsiField) { |
| return searchReferencesAndProcessAssignmentTarget(from, null, parent, processor); |
| } |
| |
| if (from instanceof PsiMethod) { |
| PsiMethod method = (PsiMethod)from; |
| |
| Collection<PsiMethod> superMethods = new THashSet<PsiMethod>(Arrays.asList(method.findDeepestSuperMethods())); |
| superMethods.add(method); |
| final Set<PsiReference> processed = new THashSet<PsiReference>(); //usages of super method and overridden method can overlap |
| for (final PsiMethod containingMethod : superMethods) { |
| if (!MethodReferencesSearch.search(containingMethod, parent.getScope().toSearchScope(), true).forEach(new Processor<PsiReference>() { |
| @Override |
| public boolean process(final PsiReference reference) { |
| ProgressManager.checkCanceled(); |
| synchronized (processed) { |
| if (!processed.add(reference)) return true; |
| } |
| PsiElement element = reference.getElement().getParent(); |
| |
| return processAssignmentTarget(element, parent, processor); |
| } |
| })) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| private static boolean searchReferencesAndProcessAssignmentTarget(@NotNull PsiElement element, @Nullable final PsiElement context, final SliceUsage parent, |
| final Processor<SliceUsage> processor) { |
| return ReferencesSearch.search(element).forEach(new Processor<PsiReference>() { |
| @Override |
| public boolean process(PsiReference reference) { |
| PsiElement element = reference.getElement(); |
| if (context != null && element.getTextOffset() < context.getTextOffset()) return true; |
| return processAssignmentTarget(element, parent, processor); |
| } |
| }); |
| } |
| |
| private static boolean processAssignmentTarget(PsiElement element, final SliceUsage parent, final Processor<SliceUsage> processor) { |
| if (!parent.params.scope.contains(element)) return true; |
| if (element instanceof PsiCompiledElement) element = element.getNavigationElement(); |
| Pair<PsiElement, PsiSubstitutor> pair = getAssignmentTarget(element, parent); |
| if (pair != null) { |
| SliceUsage usage = SliceUtil.createSliceUsage(element, parent, pair.getSecond(),parent.indexNesting, ""); |
| return processor.process(usage); |
| } |
| if (parent.params.showInstanceDereferences && isDereferenced(element)) { |
| SliceUsage usage = new SliceDereferenceUsage(element.getParent(), parent, parent.getSubstitutor()); |
| return processor.process(usage); |
| } |
| return true; |
| } |
| |
| private static boolean isDereferenced(PsiElement element) { |
| if (!(element instanceof PsiReferenceExpression)) return false; |
| PsiElement parent = element.getParent(); |
| if (!(parent instanceof PsiReferenceExpression)) return false; |
| return ((PsiReferenceExpression)parent).getQualifierExpression() == element; |
| } |
| |
| private static Pair<PsiElement,PsiSubstitutor> getAssignmentTarget(PsiElement element, SliceUsage parentUsage) { |
| element = complexify(element); |
| PsiElement target = null; |
| PsiSubstitutor substitutor = parentUsage.getSubstitutor(); |
| //assignment |
| PsiElement parent = element.getParent(); |
| if (parent instanceof PsiAssignmentExpression) { |
| PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent; |
| if (element.equals(assignment.getRExpression())) { |
| PsiElement left = assignment.getLExpression(); |
| if (left instanceof PsiReferenceExpression) { |
| JavaResolveResult result = ((PsiReferenceExpression)left).advancedResolve(false); |
| target = result.getElement(); |
| substitutor = result.getSubstitutor(); |
| } |
| } |
| } |
| else if (parent instanceof PsiVariable) { |
| PsiVariable variable = (PsiVariable)parent; |
| |
| PsiElement initializer = variable.getInitializer(); |
| if (element.equals(initializer)) { |
| target = variable; |
| } |
| } |
| //method call |
| else if (parent instanceof PsiExpressionList && parent.getParent() instanceof PsiCallExpression) { |
| PsiExpression[] expressions = ((PsiExpressionList)parent).getExpressions(); |
| int index = ArrayUtilRt.find(expressions, element); |
| PsiCallExpression methodCall = (PsiCallExpression)parent.getParent(); |
| JavaResolveResult result = methodCall.resolveMethodGenerics(); |
| PsiMethod method = (PsiMethod)result.getElement(); |
| if (index != -1 && method != null) { |
| PsiParameter[] parameters = method.getParameterList().getParameters(); |
| if (index < parameters.length) { |
| target = parameters[index]; |
| substitutor = result.getSubstitutor(); |
| } |
| } |
| } |
| else if (parent instanceof PsiReturnStatement) { |
| PsiReturnStatement statement = (PsiReturnStatement)parent; |
| if (element.equals(statement.getReturnValue())) { |
| target = PsiTreeUtil.getParentOfType(statement, PsiMethod.class); |
| } |
| } |
| |
| return target == null ? null : Pair.create(target, substitutor); |
| } |
| |
| @NotNull |
| public static PsiElement complexify(@NotNull PsiElement element) { |
| PsiElement parent = element.getParent(); |
| if (parent instanceof PsiParenthesizedExpression && element.equals(((PsiParenthesizedExpression)parent).getExpression())) { |
| return complexify(parent); |
| } |
| if (parent instanceof PsiTypeCastExpression && element.equals(((PsiTypeCastExpression)parent).getOperand())) { |
| return complexify(parent); |
| } |
| return element; |
| } |
| } |