blob: 663a76c66f42b99efc23d114b13d97da85effdd4 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
/*
* @author max
*/
package com.intellij.psi.impl.source.resolve;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JavaResolveUtil {
public static PsiClass getContextClass(PsiElement element) {
PsiElement scope = element.getContext();
while (scope != null) {
if (scope instanceof PsiClass) return (PsiClass)scope;
scope = scope.getContext();
}
return null;
}
public static PsiElement findParentContextOfClass(PsiElement element, Class aClass, boolean strict){
PsiElement scope = strict ? element.getContext() : element;
while(scope != null && !aClass.isInstance(scope)){
scope = scope.getContext();
}
return scope;
}
public static boolean isAccessible(@NotNull PsiMember member,
@Nullable PsiClass memberClass,
@Nullable PsiModifierList modifierList,
@NotNull PsiElement place,
@Nullable PsiClass accessObjectClass,
@Nullable PsiElement fileResolveScope) {
return isAccessible(member, memberClass, modifierList, place, accessObjectClass, fileResolveScope, place.getContainingFile());
}
public static boolean isAccessible(@NotNull PsiMember member,
@Nullable PsiClass memberClass,
@Nullable PsiModifierList modifierList,
@NotNull PsiElement place,
@Nullable PsiClass accessObjectClass,
@Nullable PsiElement fileResolveScope,
@Nullable PsiFile placeFile) {
if (modifierList == null || isInJavaDoc(place)) {
return true;
}
if (placeFile instanceof JavaCodeFragment) {
JavaCodeFragment fragment = (JavaCodeFragment)placeFile;
JavaCodeFragment.VisibilityChecker visibilityChecker = fragment.getVisibilityChecker();
if (visibilityChecker != null) {
JavaCodeFragment.VisibilityChecker.Visibility visibility = visibilityChecker.isDeclarationVisible(member, place);
if (visibility == JavaCodeFragment.VisibilityChecker.Visibility.VISIBLE) return true;
if (visibility == JavaCodeFragment.VisibilityChecker.Visibility.NOT_VISIBLE) return false;
}
}
else if (ignoreReferencedElementAccessibility(placeFile)) {
return true;
}
if (accessObjectClass != null) {
PsiClass containingClass = accessObjectClass.getContainingClass();
if (!isAccessible(accessObjectClass, containingClass, accessObjectClass.getModifierList(), place, null, null, placeFile)) {
return false;
}
}
PsiFile file = placeFile == null ? null : FileContextUtil.getContextFile(placeFile); //TODO: implementation method!!!!
if (PsiImplUtil.isInServerPage(file) && PsiImplUtil.isInServerPage(member.getContainingFile())) {
return true;
}
int effectiveAccessLevel = PsiUtil.getAccessLevel(modifierList);
if (ignoreReferencedElementAccessibility(file) || effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC) {
return true;
}
PsiManager manager = member.getManager();
JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) {
if (facade.arePackagesTheSame(member, place)) {
return true;
}
if (memberClass == null) {
return false;
}
for (PsiElement placeParent = place; placeParent != null; placeParent = placeParent.getContext()) {
if (placeParent instanceof PsiClass && InheritanceUtil.isInheritorOrSelf((PsiClass)placeParent, memberClass, true)) {
if (member instanceof PsiClass ||
modifierList.hasModifierProperty(PsiModifier.STATIC) ||
accessObjectClass == null ||
InheritanceUtil.isInheritorOrSelf(accessObjectClass, (PsiClass)placeParent, true)) {
return true;
}
}
}
return false;
}
if (effectiveAccessLevel == PsiUtil.ACCESS_LEVEL_PRIVATE) {
if (memberClass == null) return true;
if (accessObjectClass != null) {
PsiClass topMemberClass = getTopLevelClass(memberClass, accessObjectClass);
PsiClass topAccessClass = getTopLevelClass(accessObjectClass, memberClass);
if (!manager.areElementsEquivalent(topMemberClass, topAccessClass)) return false;
if (accessObjectClass instanceof PsiAnonymousClass && accessObjectClass.isInheritor(memberClass, true)) {
if (place instanceof PsiMethodCallExpression) {
return false;
}
}
}
if (fileResolveScope == null) {
PsiClass placeTopLevelClass = getTopLevelClass(place, null);
PsiClass memberTopLevelClass = getTopLevelClass(memberClass, null);
return manager.areElementsEquivalent(placeTopLevelClass, memberTopLevelClass);
}
else {
return fileResolveScope instanceof PsiClass &&
!((PsiClass)fileResolveScope).isInheritor(memberClass, true);
}
}
if (!facade.arePackagesTheSame(member, place)) return false;
//if (modifierList.hasModifierProperty(PsiModifier.STATIC)) return true;
// maybe inheritance lead through package local class in other package ?
final PsiClass placeClass = getContextClass(place);
if (memberClass == null || placeClass == null) return true;
// check only classes since interface members are public, and if placeClass is interface,
// then its members are static, and cannot refer to non-static members of memberClass
if (memberClass.isInterface() || placeClass.isInterface()) return true;
PsiClass clazz = accessObjectClass != null ?
accessObjectClass :
placeClass.getSuperClass(); //may start from super class
if (clazz != null && clazz.isInheritor(memberClass, true)) {
PsiClass superClass = clazz;
while (!manager.areElementsEquivalent(superClass, memberClass)) {
if (superClass == null || !facade.arePackagesTheSame(superClass, memberClass)) return false;
superClass = superClass.getSuperClass();
}
}
return true;
}
private static boolean ignoreReferencedElementAccessibility(PsiFile placeFile) {
return placeFile instanceof FileResolveScopeProvider &&
((FileResolveScopeProvider) placeFile).ignoreReferencedElementAccessibility() &&
!PsiImplUtil.isInServerPage(placeFile);
}
public static boolean isInJavaDoc(final PsiElement place) {
PsiElement scope = place;
while(scope != null){
if (scope instanceof PsiDocComment) return true;
if (scope instanceof PsiMember || scope instanceof PsiMethodCallExpression || scope instanceof PsiFile) return false;
scope = scope.getContext();
}
return false;
}
private static PsiClass getTopLevelClass(@NotNull PsiElement place, PsiClass memberClass) {
PsiClass lastClass = null;
Boolean isAtLeast17 = null;
for (PsiElement placeParent = place; placeParent != null; placeParent = placeParent.getContext()) {
if (placeParent instanceof PsiClass && !(placeParent instanceof PsiAnonymousClass)) {
final boolean isTypeParameter = placeParent instanceof PsiTypeParameter;
if (isTypeParameter && isAtLeast17 == null) {
isAtLeast17 = JavaVersionService.getInstance().isAtLeast(placeParent, JavaSdkVersion.JDK_1_7);
}
if (!isTypeParameter || isAtLeast17) {
PsiClass aClass = (PsiClass)placeParent;
if (memberClass != null && aClass.isInheritor(memberClass, true)) return aClass;
lastClass = aClass;
}
}
}
return lastClass;
}
public static boolean processImplicitlyImportedPackages(final PsiScopeProcessor processor,
final ResolveState state,
final PsiElement place,
PsiManager manager) {
PsiPackage defaultPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage("");
if (defaultPackage != null) {
if (!defaultPackage.processDeclarations(processor, state, null, place)) return false;
}
PsiPackage langPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage(CommonClassNames.DEFAULT_PACKAGE);
if (langPackage != null) {
if (!langPackage.processDeclarations(processor, state, null, place)) return false;
}
return true;
}
public static void substituteResults(@NotNull final PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult[] result) {
if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
for (int i = 0; i < result.length; i++) {
final CandidateInfo resolveResult = (CandidateInfo)result[i];
final PsiElement resultElement = resolveResult.getElement();
if (resultElement instanceof PsiClass && ((PsiClass)resultElement).hasTypeParameters()) {
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
result[i] = new CandidateInfo(resolveResult, substitutor) {
@NotNull
@Override
public PsiSubstitutor getSubstitutor() {
final PsiType[] parameters = ref.getTypeParameters();
return super.getSubstitutor().putAll((PsiClass)resultElement, parameters);
}
};
}
}
}
}
@NotNull
public static <T extends PsiPolyVariantReference> JavaResolveResult[] resolveWithContainingFile(@NotNull T ref,
@NotNull ResolveCache.PolyVariantContextResolver<T> resolver,
boolean needToPreventRecursion,
boolean incompleteCode,
@NotNull PsiFile containingFile) {
boolean valid = containingFile.isValid();
if (!valid) {
return JavaResolveResult.EMPTY_ARRAY;
}
Project project = containingFile.getProject();
ResolveResult[] results = ResolveCache.getInstance(project).resolveWithCaching(ref, resolver, needToPreventRecursion, incompleteCode,
containingFile);
return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
}
}