blob: 93b9737bdfe348c02a3e2b4a3c920de454c63865 [file] [log] [blame]
/*
* 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.usages.impl.rules;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.usages.PsiElementUsageTarget;
import com.intellij.usages.UsageTarget;
import com.intellij.util.Processor;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Set;
/**
* @author yole
*/
public class JavaUsageTypeProvider implements UsageTypeProviderEx {
@Override
public UsageType getUsageType(final PsiElement element) {
return getUsageType(element, UsageTarget.EMPTY_ARRAY);
}
@Override
public UsageType getUsageType(PsiElement element, @NotNull UsageTarget[] targets) {
UsageType classUsageType = getClassUsageType(element, targets);
if (classUsageType != null) return classUsageType;
UsageType methodUsageType = getMethodUsageType(element);
if (methodUsageType != null) return methodUsageType;
if (element instanceof PsiLiteralExpression) {
return UsageType.LITERAL_USAGE;
}
return null;
}
@Nullable
private static UsageType getMethodUsageType(PsiElement element) {
if (element instanceof PsiReferenceExpression) {
final PsiMethod containerMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (containerMethod != null) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)element;
final PsiExpression qualifier = referenceExpression.getQualifierExpression();
final PsiElement p = referenceExpression.getParent();
if (p instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)p;
final PsiMethod calledMethod = callExpression.resolveMethod();
if (calledMethod == containerMethod) {
return UsageType.RECURSION;
}
if (qualifier != null && !(qualifier instanceof PsiThisExpression) && calledMethod != null) {
if (Comparing.equal(containerMethod.getName(), calledMethod.getName()) &&
haveCommonSuperMethod(containerMethod, calledMethod)) {
boolean parametersDelegated = parametersDelegated(containerMethod, callExpression);
if (qualifier instanceof PsiSuperExpression) {
return parametersDelegated ? UsageType.DELEGATE_TO_SUPER : UsageType.DELEGATE_TO_SUPER_PARAMETERS_CHANGED;
}
else {
return parametersDelegated ? UsageType.DELEGATE_TO_ANOTHER_INSTANCE : UsageType.DELEGATE_TO_ANOTHER_INSTANCE_PARAMETERS_CHANGED;
}
}
}
}
}
}
return null;
}
private static boolean parametersDelegated(final PsiMethod method, final PsiMethodCallExpression call) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiExpression[] arguments = call.getArgumentList().getExpressions();
if (parameters.length != arguments.length) return false;
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
PsiExpression argument = arguments[i];
if (!(argument instanceof PsiReferenceExpression)) return false;
if (!((PsiReferenceExpression)argument).isReferenceTo(parameter)) return false;
}
for (PsiParameter parameter : parameters) {
if (HighlightControlFlowUtil.isAssigned(parameter)) return false;
}
return true;
}
private static boolean haveCommonSuperMethod(@NotNull PsiMethod m1, @NotNull PsiMethod m2) {
final Queue<PsiMethod> supers1Q = new ArrayDeque<PsiMethod>();
supers1Q.add(m1);
final Queue<PsiMethod> supers2Q = new ArrayDeque<PsiMethod>();
supers2Q.add(m2);
Set<PsiMethod> supers1 = new THashSet<PsiMethod>();
Set<PsiMethod> supers2 = new THashSet<PsiMethod>();
while (true) {
PsiMethod me1;
if ((me1 = supers1Q.poll()) != null) {
if (supers2.contains(me1)) return true;
supers1.add(me1);
PsiSuperMethodImplUtil.processDirectSuperMethodsSmart(me1, new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod psiMethod) {
supers1Q.add(psiMethod);
return true;
}
});
}
PsiMethod me2;
if ((me2 = supers2Q.poll()) != null) {
if (supers1.contains(me2)) return true;
supers2.add(me2);
PsiSuperMethodImplUtil.processDirectSuperMethodsSmart(me2, new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod psiMethod) {
supers2Q.add(psiMethod);
return true;
}
});
}
if (me1 == null && me2 == null) break;
}
return false;
/*
HashSet<PsiMethod> s1 = new HashSet<PsiMethod>(Arrays.asList(m1.findDeepestSuperMethods()));
s1.add(m1);
HashSet<PsiMethod> s2 = new HashSet<PsiMethod>(Arrays.asList(m2.findDeepestSuperMethods()));
s2.add(m2);
s1.retainAll(s2);
return !s1.isEmpty();
*/
}
@Nullable
private static UsageType getClassUsageType(@NotNull PsiElement element, @NotNull UsageTarget[] targets) {
final PsiJavaCodeReferenceElement codeReference = PsiTreeUtil.getParentOfType(element, PsiJavaCodeReferenceElement.class);
if(codeReference != null && isNestedClassOf(codeReference,targets)){
return UsageType.CLASS_NESTED_CLASS_ACCESS;
}
if (element.getParent() instanceof PsiAnnotation &&
element == ((PsiAnnotation)element.getParent()).getNameReferenceElement()) {
return UsageType.ANNOTATION;
}
if (PsiTreeUtil.getParentOfType(element, PsiImportStatement.class, false) != null) return UsageType.CLASS_IMPORT;
PsiReferenceList referenceList = PsiTreeUtil.getParentOfType(element, PsiReferenceList.class);
if (referenceList != null) {
if (referenceList.getParent() instanceof PsiClass) return UsageType.CLASS_EXTENDS_IMPLEMENTS_LIST;
if (referenceList.getParent() instanceof PsiMethod) return UsageType.CLASS_METHOD_THROWS_LIST;
}
if (PsiTreeUtil.getParentOfType(element, PsiTypeParameterList.class) != null ||
PsiTreeUtil.getParentOfType(element, PsiReferenceParameterList.class) != null) {
return UsageType.TYPE_PARAMETER;
}
PsiTypeCastExpression castExpression = PsiTreeUtil.getParentOfType(element, PsiTypeCastExpression.class);
if (castExpression != null) {
if (PsiTreeUtil.isAncestor(castExpression.getCastType(), element, true)) return UsageType.CLASS_CAST_TO;
}
PsiInstanceOfExpression instanceOfExpression = PsiTreeUtil.getParentOfType(element, PsiInstanceOfExpression.class);
if (instanceOfExpression != null) {
if (PsiTreeUtil.isAncestor(instanceOfExpression.getCheckType(), element, true)) return UsageType.CLASS_INSTANCE_OF;
}
if (PsiTreeUtil.getParentOfType(element, PsiClassObjectAccessExpression.class) != null) return UsageType.CLASS_CLASS_OBJECT_ACCESS;
if (element instanceof PsiReferenceExpression) {
PsiReferenceExpression expression = (PsiReferenceExpression)element;
if (expression.resolve() instanceof PsiClass) {
return UsageType.CLASS_STATIC_MEMBER_ACCESS;
}
}
final PsiParameter psiParameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class);
if (psiParameter != null) {
final PsiElement scope = psiParameter.getDeclarationScope();
if (scope instanceof PsiMethod) return UsageType.CLASS_METHOD_PARAMETER_DECLARATION;
if (scope instanceof PsiCatchSection) return UsageType.CLASS_CATCH_CLAUSE_PARAMETER_DECLARATION;
if (scope instanceof PsiForeachStatement) return UsageType.CLASS_LOCAL_VAR_DECLARATION;
return null;
}
PsiField psiField = PsiTreeUtil.getParentOfType(element, PsiField.class);
if (psiField != null) {
if (PsiTreeUtil.isAncestor(psiField.getTypeElement(), element, true)) return UsageType.CLASS_FIELD_DECLARATION;
}
PsiLocalVariable psiLocalVar = PsiTreeUtil.getParentOfType(element, PsiLocalVariable.class);
if (psiLocalVar != null) {
if (PsiTreeUtil.isAncestor(psiLocalVar.getTypeElement(), element, true)) return UsageType.CLASS_LOCAL_VAR_DECLARATION;
}
PsiMethod psiMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (psiMethod != null) {
final PsiTypeElement retType = psiMethod.getReturnTypeElement();
if (retType != null && PsiTreeUtil.isAncestor(retType, element, true)) return UsageType.CLASS_METHOD_RETURN_TYPE;
}
final PsiNewExpression psiNewExpression = PsiTreeUtil.getParentOfType(element, PsiNewExpression.class);
if (psiNewExpression != null) {
final PsiJavaCodeReferenceElement classReference = psiNewExpression.getClassOrAnonymousClassReference();
if (classReference != null && PsiTreeUtil.isAncestor(classReference, element, false)) {
if (isAnonymousClassOf(psiNewExpression.getAnonymousClass(), targets)) {
return UsageType.CLASS_ANONYMOUS_NEW_OPERATOR;
}
if (isNewArrayCreation(psiNewExpression)) {
return UsageType.CLASS_NEW_ARRAY;
}
return UsageType.CLASS_NEW_OPERATOR;
}
}
return null;
}
private static boolean isNewArrayCreation(@NotNull PsiNewExpression expression){
return expression.getArrayDimensions().length > 0 || expression.getArrayInitializer() != null;
}
private static boolean isAnonymousClassOf(@Nullable PsiAnonymousClass anonymousClass, @NotNull UsageTarget[] targets) {
if (anonymousClass == null) {
return false;
}
return qualifiesToTargetClasses(anonymousClass.getBaseClassReference(), targets);
}
private static boolean isNestedClassOf(PsiJavaCodeReferenceElement classReference, @NotNull UsageTarget[] targets) {
final PsiElement qualifier = classReference.getQualifier();
if (qualifier instanceof PsiJavaCodeReferenceElement) {
return qualifiesToTargetClasses((PsiJavaCodeReferenceElement)qualifier, targets);
}
return false;
}
private static boolean qualifiesToTargetClasses(@NotNull PsiJavaCodeReferenceElement qualifier, @NotNull UsageTarget[] targets) {
for (UsageTarget target : targets) {
if (target instanceof PsiElementUsageTarget) {
PsiElement element = ((PsiElementUsageTarget)target).getElement();
if (element instanceof PsiClass) {
if (Comparing.equal(qualifier.getReferenceName(), target.getName())) {
return true;
}
}
}
}
return false;
}
}