2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 package com.intellij.psi.impl.source.resolve;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.projectRoots.JavaSdkVersion;
24 import com.intellij.openapi.projectRoots.JavaVersionService;
25 import com.intellij.psi.*;
26 import com.intellij.psi.impl.PsiImplUtil;
27 import com.intellij.psi.infos.CandidateInfo;
28 import com.intellij.psi.javadoc.PsiDocComment;
29 import com.intellij.psi.scope.PsiScopeProcessor;
30 import com.intellij.psi.util.InheritanceUtil;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.psi.util.PsiUtil;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
36 public class JavaResolveUtil {
37 public static PsiClass getContextClass(@NotNull PsiElement element) {
38 PsiElement prev = element;
39 PsiElement scope = element.getContext();
40 while (scope != null) {
41 // skip the class if coming from its extends/implements list: those references only rely on the outer context for resolve
42 if (scope instanceof PsiClass && (prev instanceof PsiMember || prev instanceof PsiDocComment)) {
43 return (PsiClass)scope;
46 scope = scope.getContext();
51 public static PsiElement findParentContextOfClass(PsiElement element, Class aClass, boolean strict){
52 PsiElement scope = strict ? element.getContext() : element;
53 while(scope != null && !aClass.isInstance(scope)){
54 scope = scope.getContext();
59 public static boolean isAccessible(@NotNull PsiMember member,
60 @Nullable PsiClass memberClass,
61 @Nullable PsiModifierList modifierList,
62 @NotNull PsiElement place,
63 @Nullable PsiClass accessObjectClass,
64 @Nullable PsiElement fileResolveScope) {
65 return isAccessible(member, memberClass, modifierList, place, accessObjectClass, fileResolveScope, place.getContainingFile());
68 public static boolean isAccessible(@NotNull PsiMember member,
69 @Nullable PsiClass memberClass,
70 @Nullable PsiModifierList modifierList,
71 @NotNull PsiElement place,
72 @Nullable PsiClass accessObjectClass,
73 @Nullable PsiElement fileResolveScope,
74 @Nullable PsiFile placeFile) {
75 if (modifierList == null || isInJavaDoc(place)) {
79 if (placeFile instanceof JavaCodeFragment) {
80 JavaCodeFragment fragment = (JavaCodeFragment)placeFile;
81 JavaCodeFragment.VisibilityChecker visibilityChecker = fragment.getVisibilityChecker();
82 if (visibilityChecker != null) {
83 JavaCodeFragment.VisibilityChecker.Visibility visibility = visibilityChecker.isDeclarationVisible(member, place);
84 if (visibility == JavaCodeFragment.VisibilityChecker.Visibility.VISIBLE) return true;
85 if (visibility == JavaCodeFragment.VisibilityChecker.Visibility.NOT_VISIBLE) return false;
88 else if (ignoreReferencedElementAccessibility(placeFile)) {
92 if (accessObjectClass != null) {
93 PsiClass containingClass = accessObjectClass.getContainingClass();
94 if (!isAccessible(accessObjectClass, containingClass, accessObjectClass.getModifierList(), place, null, null, placeFile)) {
99 PsiFile file = placeFile == null ? null : FileContextUtil.getContextFile(placeFile); //TODO: implementation method!!!!
100 if (PsiImplUtil.isInServerPage(file) && PsiImplUtil.isInServerPage(member.getContainingFile())) {
104 int effectiveAccessLevel = PsiUtil.getAccessLevel(modifierList);
105 if (ignoreReferencedElementAccessibility(file) || effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC) {
109 PsiManager manager = member.getManager();
110 JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
112 if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) {
113 if (facade.arePackagesTheSame(member, place)) {
116 if (memberClass == null) {
119 // if resolving supertype reference, skip its containing class with getContextClass
120 PsiClass contextClass = member instanceof PsiClass ? getContextClass(place)
121 : PsiTreeUtil.getContextOfType(place, PsiClass.class, false);
122 while (contextClass != null) {
123 if (InheritanceUtil.isInheritorOrSelf(contextClass, memberClass, true)) {
124 if (member instanceof PsiClass ||
125 modifierList.hasModifierProperty(PsiModifier.STATIC) ||
126 accessObjectClass == null ||
127 InheritanceUtil.isInheritorOrSelf(accessObjectClass, contextClass, true)) {
132 contextClass = getContextClass(contextClass);
137 if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PRIVATE) {
138 if (memberClass == null) return true;
139 if (accessObjectClass != null) {
140 PsiClass topMemberClass = getTopLevelClass(memberClass, accessObjectClass);
141 PsiClass topAccessClass = getTopLevelClass(accessObjectClass, memberClass);
142 if (!manager.areElementsEquivalent(topMemberClass, topAccessClass)) return false;
143 if (accessObjectClass instanceof PsiAnonymousClass && accessObjectClass.isInheritor(memberClass, true)) {
144 if (place instanceof PsiMethodCallExpression) {
150 if (fileResolveScope == null) {
151 PsiClass placeTopLevelClass = getTopLevelClass(place, null);
152 PsiClass memberTopLevelClass = getTopLevelClass(memberClass, null);
153 return manager.areElementsEquivalent(placeTopLevelClass, memberTopLevelClass);
156 return fileResolveScope instanceof PsiClass &&
157 !((PsiClass)fileResolveScope).isInheritor(memberClass, true);
161 if (!facade.arePackagesTheSame(member, place)) return false;
162 //if (modifierList.hasModifierProperty(PsiModifier.STATIC)) return true;
163 // maybe inheritance lead through package-private class in other package ?
164 final PsiClass placeClass = getContextClass(place);
165 if (memberClass == null || placeClass == null) return true;
166 // check only classes since interface members are public, and if placeClass is interface,
167 // then its members are static, and cannot refer to non-static members of memberClass
168 if (memberClass.isInterface() || placeClass.isInterface()) return true;
169 PsiClass clazz = accessObjectClass != null ?
171 placeClass.getSuperClass(); //may start from super class
172 if (clazz != null && clazz.isInheritor(memberClass, true)) {
173 PsiClass superClass = clazz;
174 while (!manager.areElementsEquivalent(superClass, memberClass)) {
175 if (superClass == null || !facade.arePackagesTheSame(superClass, memberClass)) return false;
176 superClass = superClass.getSuperClass();
183 private static boolean ignoreReferencedElementAccessibility(PsiFile placeFile) {
184 return placeFile instanceof FileResolveScopeProvider &&
185 ((FileResolveScopeProvider) placeFile).ignoreReferencedElementAccessibility() &&
186 !PsiImplUtil.isInServerPage(placeFile);
189 public static boolean isInJavaDoc(final PsiElement place) {
190 PsiElement scope = place;
191 while(scope != null){
192 if (scope instanceof PsiDocComment) return true;
193 if (scope instanceof PsiMember || scope instanceof PsiMethodCallExpression || scope instanceof PsiFile) return false;
194 scope = scope.getContext();
199 private static PsiClass getTopLevelClass(@NotNull PsiElement place, PsiClass memberClass) {
200 PsiClass lastClass = null;
201 Boolean isAtLeast17 = null;
202 for (PsiElement placeParent = place; placeParent != null; placeParent = placeParent.getContext()) {
203 if (placeParent instanceof PsiClass && !(placeParent instanceof PsiAnonymousClass)) {
204 final boolean isTypeParameter = placeParent instanceof PsiTypeParameter;
205 if (isTypeParameter && isAtLeast17 == null) {
206 isAtLeast17 = JavaVersionService.getInstance().isAtLeast(placeParent, JavaSdkVersion.JDK_1_7);
208 if (!isTypeParameter || isAtLeast17) {
209 PsiClass aClass = (PsiClass)placeParent;
211 if (memberClass != null && aClass.isInheritor(memberClass, true)) return aClass;
221 public static boolean processImplicitlyImportedPackages(final PsiScopeProcessor processor,
222 final ResolveState state,
223 final PsiElement place,
224 PsiManager manager) {
225 PsiPackage defaultPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage("");
226 if (defaultPackage != null) {
227 if (!defaultPackage.processDeclarations(processor, state, null, place)) return false;
230 PsiPackage langPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage(CommonClassNames.DEFAULT_PACKAGE);
231 if (langPackage != null) {
232 if (!langPackage.processDeclarations(processor, state, null, place)) return false;
238 public static void substituteResults(@NotNull final PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult[] result) {
239 if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
240 for (int i = 0; i < result.length; i++) {
241 final CandidateInfo resolveResult = (CandidateInfo)result[i];
242 final PsiElement resultElement = resolveResult.getElement();
243 if (resultElement instanceof PsiClass && ((PsiClass)resultElement).hasTypeParameters()) {
244 PsiSubstitutor substitutor = resolveResult.getSubstitutor();
245 result[i] = new CandidateInfo(resolveResult, substitutor) {
248 public PsiSubstitutor getSubstitutor() {
249 final PsiType[] parameters = ref.getTypeParameters();
250 return super.getSubstitutor().putAll((PsiClass)resultElement, parameters);
259 public static <T extends PsiPolyVariantReference> JavaResolveResult[] resolveWithContainingFile(@NotNull T ref,
260 @NotNull ResolveCache.PolyVariantContextResolver<T> resolver,
261 boolean needToPreventRecursion,
262 boolean incompleteCode,
263 @NotNull PsiFile containingFile) {
264 boolean valid = containingFile.isValid();
266 return JavaResolveResult.EMPTY_ARRAY;
268 Project project = containingFile.getProject();
269 ResolveResult[] results = ResolveCache.getInstance(project).resolveWithCaching(ref, resolver, needToPreventRecursion, incompleteCode,
271 return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;