fba9855a722930f1399968c40368dac647f3c52d
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / resolve / JavaResolveUtil.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * @author max
19  */
20 package com.intellij.psi.impl.source.resolve;
21
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;
35
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       if (scope instanceof PsiClass && (prev instanceof PsiMember || prev instanceof PsiDocComment)) {
42         return (PsiClass)scope;
43       }
44       prev = scope;
45       scope = scope.getContext();
46     }
47     return null;
48   }
49
50   public static PsiElement findParentContextOfClass(PsiElement element, Class aClass, boolean strict){
51     PsiElement scope = strict ? element.getContext() : element;
52     while(scope != null && !aClass.isInstance(scope)){
53       scope = scope.getContext();
54     }
55     return scope;
56   }
57
58   public static boolean isAccessible(@NotNull PsiMember member,
59                                      @Nullable PsiClass memberClass,
60                                      @Nullable PsiModifierList modifierList,
61                                      @NotNull PsiElement place,
62                                      @Nullable PsiClass accessObjectClass,
63                                      @Nullable PsiElement fileResolveScope) {
64     return isAccessible(member, memberClass, modifierList, place, accessObjectClass, fileResolveScope, place.getContainingFile());
65   }
66
67   public static boolean isAccessible(@NotNull PsiMember member,
68                                      @Nullable PsiClass memberClass,
69                                      @Nullable PsiModifierList modifierList,
70                                      @NotNull PsiElement place,
71                                      @Nullable PsiClass accessObjectClass,
72                                      @Nullable PsiElement fileResolveScope,
73                                      @Nullable PsiFile placeFile) {
74     if (modifierList == null || isInJavaDoc(place)) {
75       return true;
76     }
77
78     if (placeFile instanceof JavaCodeFragment) {
79       JavaCodeFragment fragment = (JavaCodeFragment)placeFile;
80       JavaCodeFragment.VisibilityChecker visibilityChecker = fragment.getVisibilityChecker();
81       if (visibilityChecker != null) {
82         JavaCodeFragment.VisibilityChecker.Visibility visibility = visibilityChecker.isDeclarationVisible(member, place);
83         if (visibility == JavaCodeFragment.VisibilityChecker.Visibility.VISIBLE) return true;
84         if (visibility == JavaCodeFragment.VisibilityChecker.Visibility.NOT_VISIBLE) return false;
85       }
86     }
87     else if (ignoreReferencedElementAccessibility(placeFile)) {
88       return true;
89     }
90
91     if (accessObjectClass != null) {
92       PsiClass containingClass = accessObjectClass.getContainingClass();
93       if (!isAccessible(accessObjectClass, containingClass, accessObjectClass.getModifierList(), place, null, null, placeFile)) {
94         return false;
95       }
96     }
97
98     PsiFile file = placeFile == null ? null : FileContextUtil.getContextFile(placeFile); //TODO: implementation method!!!!
99     if (PsiImplUtil.isInServerPage(file) && PsiImplUtil.isInServerPage(member.getContainingFile())) {
100       return true;
101     }
102
103     int effectiveAccessLevel = PsiUtil.getAccessLevel(modifierList);
104     if (ignoreReferencedElementAccessibility(file) || effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC) {
105       return true;
106     }
107
108     PsiManager manager = member.getManager();
109     JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
110
111     if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) {
112       if (facade.arePackagesTheSame(member, place)) {
113         return true;
114       }
115       if (memberClass == null) {
116         return false;
117       }
118       PsiClass contextClass = member instanceof PsiClass ? getContextClass(place)
119                                                          : PsiTreeUtil.getContextOfType(place, PsiClass.class, false);
120       while (contextClass != null) {
121         if (InheritanceUtil.isInheritorOrSelf(contextClass, memberClass, true)) {
122           if (member instanceof PsiClass ||
123               modifierList.hasModifierProperty(PsiModifier.STATIC) ||
124               accessObjectClass == null ||
125               InheritanceUtil.isInheritorOrSelf(accessObjectClass, contextClass, true)) {
126             return true;
127           }
128         }
129
130         contextClass = getContextClass(contextClass);
131       }
132       return false;
133     }
134
135     if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PRIVATE) {
136       if (memberClass == null) return true;
137       if (accessObjectClass != null) {
138         PsiClass topMemberClass = getTopLevelClass(memberClass, accessObjectClass);
139         PsiClass topAccessClass = getTopLevelClass(accessObjectClass, memberClass);
140         if (!manager.areElementsEquivalent(topMemberClass, topAccessClass)) return false;
141         if (accessObjectClass instanceof PsiAnonymousClass && accessObjectClass.isInheritor(memberClass, true)) {
142           if (place instanceof PsiMethodCallExpression) {
143             return false;
144           }
145         }
146       }
147
148       if (fileResolveScope == null) {
149         PsiClass placeTopLevelClass = getTopLevelClass(place, null);
150         PsiClass memberTopLevelClass = getTopLevelClass(memberClass, null);
151         return manager.areElementsEquivalent(placeTopLevelClass, memberTopLevelClass);
152       }
153       else {
154         return fileResolveScope instanceof PsiClass &&
155                !((PsiClass)fileResolveScope).isInheritor(memberClass, true);
156       }
157     }
158
159     if (!facade.arePackagesTheSame(member, place)) return false;
160     //if (modifierList.hasModifierProperty(PsiModifier.STATIC)) return true;
161     // maybe inheritance lead through package-private class in other package ?
162     final PsiClass placeClass = getContextClass(place);
163     if (memberClass == null || placeClass == null) return true;
164     // check only classes since interface members are public,  and if placeClass is interface,
165     // then its members are static, and cannot refer to non-static members of memberClass
166     if (memberClass.isInterface() || placeClass.isInterface()) return true;
167     PsiClass clazz = accessObjectClass != null ?
168                      accessObjectClass :
169                      placeClass.getSuperClass(); //may start from super class
170     if (clazz != null && clazz.isInheritor(memberClass, true)) {
171       PsiClass superClass = clazz;
172       while (!manager.areElementsEquivalent(superClass, memberClass)) {
173         if (superClass == null || !facade.arePackagesTheSame(superClass, memberClass)) return false;
174         superClass = superClass.getSuperClass();
175       }
176     }
177
178     return true;
179   }
180
181   private static boolean ignoreReferencedElementAccessibility(PsiFile placeFile) {
182     return placeFile instanceof FileResolveScopeProvider &&
183            ((FileResolveScopeProvider) placeFile).ignoreReferencedElementAccessibility() &&
184            !PsiImplUtil.isInServerPage(placeFile);
185   }
186
187   public static boolean isInJavaDoc(final PsiElement place) {
188     PsiElement scope = place;
189     while(scope != null){
190       if (scope instanceof PsiDocComment) return true;
191       if (scope instanceof PsiMember || scope instanceof PsiMethodCallExpression || scope instanceof PsiFile) return false;
192       scope = scope.getContext();
193     }
194     return false;
195   }
196
197   private static PsiClass getTopLevelClass(@NotNull PsiElement place, PsiClass memberClass) {
198     PsiClass lastClass = null;
199     Boolean isAtLeast17 = null;
200     for (PsiElement placeParent = place; placeParent != null; placeParent = placeParent.getContext()) {
201       if (placeParent instanceof PsiClass && !(placeParent instanceof PsiAnonymousClass)) {
202         final boolean isTypeParameter = placeParent instanceof PsiTypeParameter;
203         if (isTypeParameter && isAtLeast17 == null) {
204           isAtLeast17 = JavaVersionService.getInstance().isAtLeast(placeParent, JavaSdkVersion.JDK_1_7);
205         }
206         if (!isTypeParameter || isAtLeast17) {
207           PsiClass aClass = (PsiClass)placeParent;
208
209           if (memberClass != null && aClass.isInheritor(memberClass, true)) return aClass;
210
211           lastClass = aClass;
212         }
213       }
214     }
215
216     return lastClass;
217   }
218
219   public static boolean processImplicitlyImportedPackages(final PsiScopeProcessor processor,
220                                                           final ResolveState state,
221                                                           final PsiElement place,
222                                                           PsiManager manager) {
223     PsiPackage defaultPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage("");
224     if (defaultPackage != null) {
225       if (!defaultPackage.processDeclarations(processor, state, null, place)) return false;
226     }
227
228     PsiPackage langPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage(CommonClassNames.DEFAULT_PACKAGE);
229     if (langPackage != null) {
230       if (!langPackage.processDeclarations(processor, state, null, place)) return false;
231     }
232
233     return true;
234   }
235
236   public static void substituteResults(@NotNull final PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult[] result) {
237     if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
238       for (int i = 0; i < result.length; i++) {
239         final CandidateInfo resolveResult = (CandidateInfo)result[i];
240         final PsiElement resultElement = resolveResult.getElement();
241         if (resultElement instanceof PsiClass && ((PsiClass)resultElement).hasTypeParameters()) {
242           PsiSubstitutor substitutor = resolveResult.getSubstitutor();
243           result[i] = new CandidateInfo(resolveResult, substitutor) {
244             @NotNull
245             @Override
246             public PsiSubstitutor getSubstitutor() {
247               final PsiType[] parameters = ref.getTypeParameters();
248               return super.getSubstitutor().putAll((PsiClass)resultElement, parameters);
249             }
250           };
251         }
252       }
253     }
254   }
255
256   @NotNull
257   public static <T extends PsiPolyVariantReference> JavaResolveResult[] resolveWithContainingFile(@NotNull T ref,
258                                                                                                   @NotNull ResolveCache.PolyVariantContextResolver<T> resolver,
259                                                                                                   boolean needToPreventRecursion,
260                                                                                                   boolean incompleteCode,
261                                                                                                   @NotNull PsiFile containingFile) {
262     boolean valid = containingFile.isValid();
263     if (!valid) {
264       return JavaResolveResult.EMPTY_ARRAY;
265     }
266     Project project = containingFile.getProject();
267     ResolveResult[] results = ResolveCache.getInstance(project).resolveWithCaching(ref, resolver, needToPreventRecursion, incompleteCode,
268                                                                                    containingFile);
269     return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
270   }
271 }