/* * Copyright 2000-2011 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. */ /** * Created by IntelliJ IDEA. * User: igork * Date: Nov 25, 2002 * Time: 2:05:49 PM * To change this template use Options | File Templates. */ package com.intellij.psi.scope.util; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.JavaResolveUtil; import com.intellij.psi.scope.JavaScopeProcessorEvent; import com.intellij.psi.scope.MethodProcessorSetupFailedException; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.scope.processor.MethodsProcessor; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; public class PsiScopesUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.scope.util.PsiScopesUtil"); private PsiScopesUtil() { } public static boolean treeWalkUp(@NotNull PsiScopeProcessor processor, @NotNull PsiElement entrance, @Nullable PsiElement maxScope) { return treeWalkUp(processor, entrance, maxScope, ResolveState.initial()); } public static boolean treeWalkUp(@NotNull final PsiScopeProcessor processor, @NotNull final PsiElement entrance, @Nullable final PsiElement maxScope, @NotNull final ResolveState state) { if (!entrance.isValid()) { LOG.error(new PsiInvalidElementAccessException(entrance)); } PsiElement prevParent = entrance; PsiElement scope = entrance; while (scope != null) { ProgressIndicatorProvider.checkCanceled(); if (scope instanceof PsiClass) { processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, scope); } if (!scope.processDeclarations(processor, state, prevParent, entrance)) { return false; // resolved } if (scope instanceof PsiModifierListOwner && !(scope instanceof PsiParameter/* important for not loading tree! */)) { PsiModifierList modifierList = ((PsiModifierListOwner)scope).getModifierList(); if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC)) { processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null); } } if (scope == maxScope) break; prevParent = scope; scope = prevParent.getContext(); processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null); } return true; } public static boolean walkChildrenScopes(@NotNull PsiElement thisElement, @NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, PsiElement place) { PsiElement child = null; if (lastParent != null && lastParent.getParent() == thisElement) { child = lastParent.getPrevSibling(); if (child == null) return true; // first element } if (child == null) { child = thisElement.getLastChild(); } while (child != null) { if (!child.processDeclarations(processor, state, null, place)) return false; child = child.getPrevSibling(); } return true; } public static void processTypeDeclarations(PsiType type, PsiElement place, PsiScopeProcessor processor) { if (type instanceof PsiArrayType) { LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place); final PsiClass arrayClass = JavaPsiFacade.getInstance(place.getProject()).getElementFactory().getArrayClass(languageLevel); final PsiTypeParameter[] arrayTypeParameters = arrayClass.getTypeParameters(); PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; if (arrayTypeParameters.length > 0) { substitutor = substitutor.put(arrayTypeParameters[0], ((PsiArrayType)type).getComponentType()); } arrayClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor), arrayClass, place); } else if (type instanceof PsiIntersectionType) { for (PsiType psiType : ((PsiIntersectionType)type).getConjuncts()) { processTypeDeclarations(psiType, place, processor); } } else if (type instanceof PsiDisjunctionType) { final PsiType lub = ((PsiDisjunctionType)type).getLeastUpperBound(); processTypeDeclarations(lub, place, processor); } else { final JavaResolveResult result = PsiUtil.resolveGenericsClassInType(type); final PsiClass clazz = (PsiClass)result.getElement(); if (clazz != null) { clazz.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, result.getSubstitutor()), clazz, place); } } } public static boolean resolveAndWalk(@NotNull PsiScopeProcessor processor, @NotNull PsiJavaCodeReferenceElement ref, @Nullable PsiElement maxScope) { return resolveAndWalk(processor, ref, maxScope, false); } public static boolean resolveAndWalk(@NotNull PsiScopeProcessor processor, @NotNull PsiJavaCodeReferenceElement ref, @Nullable PsiElement maxScope, boolean incompleteCode) { final PsiElement qualifier = ref.getQualifier(); final PsiElement classNameElement = ref.getReferenceNameElement(); if (classNameElement == null) return true; if (qualifier != null) { // Composite expression PsiElement target = null; PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; if (qualifier instanceof PsiExpression || qualifier instanceof PsiJavaCodeReferenceElement) { PsiType type = null; if (qualifier instanceof PsiExpression) { type = ((PsiExpression)qualifier).getType(); if (type != null) { assert type.isValid() : type.getClass() + "; " + qualifier; } processTypeDeclarations(type, ref, processor); } if (type == null && qualifier instanceof PsiJavaCodeReferenceElement) { // In case of class qualifier final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)qualifier; final JavaResolveResult result = referenceElement.advancedResolve(incompleteCode); target = result.getElement(); substitutor = result.getSubstitutor(); if (target instanceof PsiVariable) { type = substitutor.substitute(((PsiVariable)target).getType()); if (type instanceof PsiClassType) { final JavaResolveResult typeResult = ((PsiClassType)type).resolveGenerics(); target = typeResult.getElement(); substitutor = substitutor.putAll(typeResult.getSubstitutor()); } else { target = null; } } else if (target instanceof PsiMethod) { type = substitutor.substitute(((PsiMethod)target).getReturnType()); if (type instanceof PsiClassType) { final JavaResolveResult typeResult = ((PsiClassType)type).resolveGenerics(); target = typeResult.getElement(); substitutor = substitutor.putAll(typeResult.getSubstitutor()); } else { target = null; } final PsiType[] types = referenceElement.getTypeParameters(); if (target instanceof PsiClass) { substitutor = substitutor.putAll((PsiClass)target, types); } } else if (target instanceof PsiClass) { processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null); } } } if (target != null) { return target.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor), target, ref); } } else { // simple expression -> trying to resolve variable or method return treeWalkUp(processor, ref, maxScope); } return true; } public static void setupAndRunProcessor(@NotNull MethodsProcessor processor, @NotNull PsiCallExpression call, boolean dummyImplicitConstructor) throws MethodProcessorSetupFailedException { if (call instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)call; final PsiJavaCodeReferenceElement ref = methodCall.getMethodExpression(); processor.setArgumentList(methodCall.getArgumentList()); processor.obtainTypeArguments(methodCall); if (!ref.isQualified() || ref.getReferenceNameElement() instanceof PsiKeyword) { final PsiElement referenceNameElement = ref.getReferenceNameElement(); if (referenceNameElement == null) return; if (referenceNameElement instanceof PsiKeyword) { final PsiKeyword keyword = (PsiKeyword)referenceNameElement; if (keyword.getTokenType() == JavaTokenType.THIS_KEYWORD) { final PsiClass aClass = JavaResolveUtil.getContextClass(methodCall); if (aClass == null) { throw new MethodProcessorSetupFailedException("Can't resolve class for this expression"); } processor.setIsConstructor(true); processor.setAccessClass(aClass); aClass.processDeclarations(processor, ResolveState.initial(), null, call); if (dummyImplicitConstructor) { processDummyConstructor(processor, aClass); } } else if (keyword.getTokenType() == JavaTokenType.SUPER_KEYWORD) { PsiClass aClass = JavaResolveUtil.getContextClass(methodCall); if (aClass == null) { throw new MethodProcessorSetupFailedException("Can't resolve class for super expression"); } final PsiClass superClass = aClass.getSuperClass(); if (superClass != null) { PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; PsiClass runSuper = superClass; List<PsiSubstitutor> contextSubstitutors = new ArrayList<PsiSubstitutor>(); do { if (runSuper != null) { PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(runSuper, aClass, PsiSubstitutor.EMPTY); contextSubstitutors.add(superSubstitutor); } if (aClass.hasModifierProperty(PsiModifier.STATIC)) break; aClass = JavaResolveUtil.getContextClass(aClass); if (aClass != null) runSuper = aClass.getSuperClass(); } while (aClass != null); //apply substitutors in 'outer classes down to inner classes' order because inner class subst take precedence for (int i = contextSubstitutors.size() - 1; i >= 0; i--) { PsiSubstitutor contextSubstitutor = contextSubstitutors.get(i); substitutor = substitutor.putAll(contextSubstitutor); } processor.setIsConstructor(true); processor.setAccessClass(null); final PsiMethod[] constructors = superClass.getConstructors(); ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor); for (PsiMethod constructor : constructors) { if (!processor.execute(constructor, state)) return; } if (dummyImplicitConstructor) processDummyConstructor(processor, superClass); } } else { LOG.error("Unknown name element " + referenceNameElement + " in reference " + ref.getText() + "(" + ref + ")"); } } else if (referenceNameElement instanceof PsiIdentifier) { processor.setIsConstructor(false); processor.setName(referenceNameElement.getText()); processor.setAccessClass(null); resolveAndWalk(processor, ref, null); } else { LOG.error("Unknown name element " + referenceNameElement + " in reference " + ref.getText() + "(" + ref + ")"); } } else { // Complex expression final PsiElement referenceName = methodCall.getMethodExpression().getReferenceNameElement(); final PsiManager manager = call.getManager(); final PsiElement qualifier = ref.getQualifier(); if (referenceName == null) { // e.g. "manager.(beginTransaction)" throw new MethodProcessorSetupFailedException("Can't resolve method name for this expression"); } if (referenceName instanceof PsiIdentifier && qualifier instanceof PsiExpression) { PsiType type = ((PsiExpression)qualifier).getType(); if (type != null && qualifier instanceof PsiReferenceExpression) { final PsiElement resolve = ((PsiReferenceExpression)qualifier).resolve(); if (resolve instanceof PsiVariable && ((PsiVariable)resolve).hasModifierProperty(PsiModifier.FINAL) && ((PsiVariable)resolve).hasInitializer()) { final PsiExpression initializer = ((PsiVariable)resolve).getInitializer(); if (initializer instanceof PsiNewExpression) { final PsiAnonymousClass anonymousClass = ((PsiNewExpression)initializer).getAnonymousClass(); if (anonymousClass != null && type.equals(anonymousClass.getBaseClassType())) { type = initializer.getType(); } } } } if (type == null) { if (qualifier instanceof PsiJavaCodeReferenceElement) { final JavaResolveResult result = ((PsiJavaCodeReferenceElement)qualifier).advancedResolve(false); if (result.getElement() instanceof PsiClass) { processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null); processQualifierResult(result, processor, methodCall); } } else { throw new MethodProcessorSetupFailedException("Cant determine qualifier type!"); } } else if (type instanceof PsiIntersectionType) { final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts(); for (PsiType conjunct : conjuncts) { if (!processQualifierType(conjunct, processor, manager, methodCall)) break; } } else if (type instanceof PsiDisjunctionType) { processQualifierType(((PsiDisjunctionType)type).getLeastUpperBound(), processor, manager, methodCall); } else { processQualifierType(type, processor, manager, methodCall); } } else { LOG.error("ref: " + ref + " (" + ref.getClass() + ")," + " ref.getReferenceNameElement()=" + ref.getReferenceNameElement() + "; methodCall.getMethodExpression().getReferenceNameElement()=" + methodCall.getMethodExpression().getReferenceNameElement() + "; qualifier="+qualifier); } } } else { LOG.assertTrue(call instanceof PsiNewExpression); PsiNewExpression newExpr = (PsiNewExpression)call; PsiJavaCodeReferenceElement classRef = newExpr.getClassOrAnonymousClassReference(); if (classRef == null) { throw new MethodProcessorSetupFailedException("Cant get reference to class in new expression"); } final JavaResolveResult result = classRef.advancedResolve(false); PsiClass aClass = (PsiClass)result.getElement(); if (aClass == null) { throw new MethodProcessorSetupFailedException("Cant resolve class in new expression"); } processor.setIsConstructor(true); processor.setAccessClass(aClass); processor.setArgumentList(newExpr.getArgumentList()); processor.obtainTypeArguments(newExpr); aClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, result.getSubstitutor()), null, call); if (dummyImplicitConstructor) { processDummyConstructor(processor, aClass); } } } private static boolean processQualifierType(@NotNull final PsiType type, final MethodsProcessor processor, PsiManager manager, PsiMethodCallExpression call) throws MethodProcessorSetupFailedException { LOG.assertTrue(type.isValid()); if (type instanceof PsiClassType) { JavaResolveResult qualifierResult = ((PsiClassType)type).resolveGenerics(); return processQualifierResult(qualifierResult, processor, call); } if (type instanceof PsiArrayType) { LanguageLevel languageLevel = PsiUtil.getLanguageLevel(call); PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); JavaResolveResult qualifierResult = factory.getArrayClassType(((PsiArrayType)type).getComponentType(), languageLevel).resolveGenerics(); return processQualifierResult(qualifierResult, processor, call); } if (type instanceof PsiIntersectionType) { for (PsiType conjunct : ((PsiIntersectionType)type).getConjuncts()) { if (!processQualifierType(conjunct, processor, manager, call)) return false; } } return true; } private static boolean processQualifierResult(@NotNull JavaResolveResult qualifierResult, @NotNull MethodsProcessor processor, @NotNull PsiMethodCallExpression methodCall) throws MethodProcessorSetupFailedException { PsiElement resolve = qualifierResult.getElement(); if (resolve == null) { throw new MethodProcessorSetupFailedException("Cant determine qualifier class!"); } if (resolve instanceof PsiTypeParameter) { processor.setAccessClass((PsiClass)resolve); } else if (resolve instanceof PsiClass) { PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression(); if (!(qualifier instanceof PsiSuperExpression)) { processor.setAccessClass((PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement()); } } processor.setIsConstructor(false); processor.setName(methodCall.getMethodExpression().getReferenceName()); ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor()); return resolve.processDeclarations(processor, state, methodCall, methodCall); } private static void processDummyConstructor(MethodsProcessor processor, PsiClass aClass) { if (aClass instanceof PsiAnonymousClass) return; try { PsiMethod[] constructors = aClass.getConstructors(); if (constructors.length != 0) { return; } final PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory(); final PsiMethod dummyConstructor = factory.createConstructor(); PsiIdentifier nameIdentifier = aClass.getNameIdentifier(); if (nameIdentifier != null) { dummyConstructor.getNameIdentifier().replace(nameIdentifier); } processor.forceAddResult(dummyConstructor); } catch (IncorrectOperationException e) { LOG.error(e); } } }