blob: 341f44a2fdf3714e964b4d4d730844ab3a17a61f [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.
*/
package com.intellij.codeInspection.reference;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
import com.intellij.codeInspection.ex.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.UserDataCache;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import gnu.trove.THashMap;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author anna
* Date: 20-Dec-2007
*/
public class RefJavaManagerImpl extends RefJavaManager {
private static final Logger LOG = Logger.getInstance("#" + RefJavaManagerImpl.class.getName());
private PsiMethod myAppMainPattern;
private PsiMethod myAppPremainPattern;
private PsiMethod myAppAgentmainPattern;
private PsiClass myApplet;
private PsiClass myServlet;
private PsiClass myAndroidActivity;
private PsiClass myAndroidService;
private PsiClass myAndroidBackupAgent;
private PsiClass myAndroidFragment;
private PsiClass myAndroidV4Fragment;
private PsiClass myAndroidContentProvider;
private PsiClass myAndroidReceiver;
private PsiClass myAndroidView;
private PsiClass myAndroidActionProvider;
private PsiClass myAndroidParcelable;
private RefPackage myDefaultPackage;
private THashMap<String, RefPackage> myPackages;
private final RefManagerImpl myRefManager;
private PsiElementVisitor myProjectIterator;
private EntryPointsManager myEntryPointsManager;
public RefJavaManagerImpl(@NotNull RefManagerImpl manager) {
myRefManager = manager;
final Project project = manager.getProject();
final PsiManager psiManager = PsiManager.getInstance(project);
PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
try {
myAppMainPattern = factory.createMethodFromText("void main(String[] args);", null);
myAppPremainPattern = factory.createMethodFromText("void premain(String[] args, java.lang.instrument.Instrumentation i);", null);
myAppAgentmainPattern = factory.createMethodFromText("void agentmain(String[] args, java.lang.instrument.Instrumentation i);", null);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
GlobalSearchScope scope = GlobalSearchScope.allScope(project);
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(psiManager.getProject());
myApplet = psiFacade.findClass("java.applet.Applet", scope);
myServlet = psiFacade.findClass("javax.servlet.Servlet", scope);
// Android Framework APIs that apps extend and where the subclasses must be public
// such that the framework can instantiate them
myAndroidActivity = psiFacade.findClass("android.app.Activity", scope);
myAndroidService = psiFacade.findClass("android.app.Service", scope);
myAndroidFragment = psiFacade.findClass("android.app.Fragment", scope);
myAndroidV4Fragment = psiFacade.findClass("android.support.v4.app.Fragment", scope);
myAndroidContentProvider = psiFacade.findClass("android.content.ContentProvider", scope);
myAndroidReceiver = psiFacade.findClass("android.content.BroadcastReceiver", scope);
myAndroidView = psiFacade.findClass("android.view.View", scope);
myAndroidActionProvider = psiFacade.findClass("android.view.ActionProvider", scope);
myAndroidParcelable = psiFacade.findClass("android.os.Parcelable", scope);
myAndroidBackupAgent = psiFacade.findClass("android.app.backup.BackupAgent", scope);
}
@Override
public RefPackage getPackage(String packageName) {
if (myPackages == null) {
myPackages = new THashMap<String, RefPackage>();
}
RefPackage refPackage = myPackages.get(packageName);
if (refPackage == null) {
refPackage = new RefPackageImpl(packageName, myRefManager);
myPackages.put(packageName, refPackage);
int dotIndex = packageName.lastIndexOf('.');
if (dotIndex >= 0) {
((RefPackageImpl)getPackage(packageName.substring(0, dotIndex))).add(refPackage);
}
else {
((RefProjectImpl)myRefManager.getRefProject()).add(refPackage);
}
}
return refPackage;
}
public boolean isEntryPoint(final RefElement element) {
UnusedDeclarationInspection tool = getDeadCodeTool(element);
return tool != null && tool.isEntryPoint(element);
}
@Nullable
private UnusedDeclarationInspection getDeadCodeTool(RefElement element) {
PsiFile file = ((RefElementImpl)element).getContainingFile();
if (file == null) return null;
return getDeadCodeTool(file);
}
private static final UserDataCache<Ref<UnusedDeclarationInspection>, PsiFile, RefManagerImpl> DEAD_CODE_TOOL = new UserDataCache<Ref<UnusedDeclarationInspection>, PsiFile, RefManagerImpl>("DEAD_CODE_TOOL") {
@Override
protected Ref<UnusedDeclarationInspection> compute(PsiFile file, RefManagerImpl refManager) {
Tools tools = ((GlobalInspectionContextBase)refManager.getContext()).getTools().get(UnusedDeclarationInspection.SHORT_NAME);
InspectionToolWrapper toolWrapper = tools == null ? null : tools.getEnabledTool(file);
InspectionProfileEntry tool = toolWrapper == null ? null : toolWrapper.getTool();
return Ref.create(tool instanceof UnusedDeclarationInspection ? (UnusedDeclarationInspection)tool : null);
}
};
@Nullable
private UnusedDeclarationInspection getDeadCodeTool(PsiElement element) {
PsiFile file = element.getContainingFile();
return file != null ? DEAD_CODE_TOOL.get(file, myRefManager).get() : null;
}
@Override
public RefPackage getDefaultPackage() {
if (myDefaultPackage == null) {
myDefaultPackage = getPackage(InspectionsBundle.message("inspection.reference.default.package"));
}
return myDefaultPackage;
}
@Override
public PsiMethod getAppMainPattern() {
return myAppMainPattern;
}
@Override
public PsiMethod getAppPremainPattern() {
return myAppPremainPattern;
}
@Override
public PsiMethod getAppAgentmainPattern() {
return myAppAgentmainPattern;
}
@Override
public PsiClass getApplet() {
return myApplet;
}
@Override
public PsiClass getServlet() {
return myServlet;
}
@Override
public PsiClass getAndroidActivity() {
return myAndroidActivity;
}
@Override
public PsiClass getAndroidService() {
return myAndroidService;
}
@Override
public PsiClass getAndroidBackupAgent() {
return myAndroidBackupAgent;
}
@Override
public PsiClass getAndroidFragment(boolean support) {
return support ? myAndroidV4Fragment : myAndroidFragment;
}
@Override
public PsiClass getAndroidContentProvider() {
return myAndroidContentProvider;
}
@Override
public PsiClass getAndroidReceiver() {
return myAndroidReceiver;
}
@Override
public PsiClass getAndroidView() {
return myAndroidView;
}
@Override
public PsiClass getAndroidActionProvider() {
return myAndroidActionProvider;
}
@Override
public PsiClass getAndroidParcelable() {
return myAndroidParcelable;
}
@Override
public RefParameter getParameterReference(PsiParameter param, int index) {
LOG.assertTrue(myRefManager.isValidPointForReference(), "References may become invalid after process is finished");
RefElement ref = myRefManager.getFromRefTable(param);
if (ref == null) {
ref = new RefParameterImpl(param, index, myRefManager);
((RefParameterImpl)ref).initialize();
myRefManager.putToRefTable(param, ref);
}
return (RefParameter)ref;
}
@Override
public void iterate(@NotNull final RefVisitor visitor) {
if (myPackages != null) {
for (RefPackage refPackage : myPackages.values()) {
refPackage.accept(visitor);
}
}
for (RefElement refElement : myRefManager.getSortedElements()) {
if (refElement instanceof RefClass) {
RefClass refClass = (RefClass)refElement;
RefMethod refDefaultConstructor = refClass.getDefaultConstructor();
if (refDefaultConstructor instanceof RefImplicitConstructor) {
refClass.getDefaultConstructor().accept(visitor);
}
}
}
}
@Override
public void cleanup() {
if (myEntryPointsManager != null) {
Disposer.dispose(myEntryPointsManager);
myEntryPointsManager = null;
}
myPackages = null;
myApplet = null;
myAppMainPattern = null;
myAppPremainPattern = null;
myAppAgentmainPattern = null;
myServlet = null;
myAndroidActivity = null;
myAndroidService = null;
myAndroidBackupAgent = null;
myAndroidFragment = null;
myAndroidV4Fragment = null;
myAndroidContentProvider = null;
myAndroidReceiver = null;
myAndroidView = null;
myAndroidActionProvider = null;
myAndroidParcelable = null;
myDefaultPackage = null;
myProjectIterator = null;
}
@Override
public void removeReference(final RefElement refElement) {
if (refElement instanceof RefMethod) {
RefMethod refMethod = (RefMethod)refElement;
RefParameter[] params = refMethod.getParameters();
for (RefParameter param : params) {
myRefManager.removeReference(param);
}
}
}
@Override
@Nullable
public RefElement createRefElement(final PsiElement elem) {
if (elem instanceof PsiClass) {
return new RefClassImpl((PsiClass)elem, myRefManager);
}
else if (elem instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)elem;
final RefElement ref = myRefManager.getReference(method.getContainingClass(), true);
if (ref instanceof RefClass) {
return new RefMethodImpl((RefClass)ref, method, myRefManager);
}
}
else if (elem instanceof PsiField) {
final PsiField field = (PsiField)elem;
final RefElement ref = myRefManager.getReference(field.getContainingClass(), true);
if (ref instanceof RefClass) {
return new RefFieldImpl((RefClass)ref, field, myRefManager);
}
}
else if (elem instanceof PsiJavaFile) {
return new RefJavaFileImpl((PsiJavaFile)elem, myRefManager);
}
return null;
}
@Override
@Nullable
public RefEntity getReference(final String type, final String fqName) {
if (METHOD.equals(type)) {
return RefMethodImpl.methodFromExternalName(myRefManager, fqName);
}
else if (CLASS.equals(type)) {
return RefClassImpl.classFromExternalName(myRefManager, fqName);
}
else if (FIELD.equals(type)) {
return RefFieldImpl.fieldFromExternalName(myRefManager, fqName);
}
else if (PARAMETER.equals(type)) {
return RefParameterImpl.parameterFromExternalName(myRefManager, fqName);
}
else if (PACKAGE.equals(type)) {
return RefPackageImpl.packageFromFQName(myRefManager, fqName);
}
return null;
}
@Override
@Nullable
public String getType(final RefEntity ref) {
if (ref instanceof RefMethod) {
return METHOD;
}
else if (ref instanceof RefClass) {
return CLASS;
}
else if (ref instanceof RefField) {
return FIELD;
}
else if (ref instanceof RefParameter) {
return PARAMETER;
}
else if (ref instanceof RefPackage) {
return PACKAGE;
}
return null;
}
@NotNull
@Override
public RefEntity getRefinedElement(@NotNull final RefEntity ref) {
if (ref instanceof RefImplicitConstructor) {
return ((RefImplicitConstructor)ref).getOwnerClass();
}
return ref;
}
@Override
public void visitElement(final PsiElement element) {
if (myProjectIterator == null) {
myProjectIterator = new MyJavaElementVisitor();
}
element.accept(myProjectIterator);
}
@Override
@Nullable
public String getGroupName(final RefEntity entity) {
if (entity instanceof RefFile && !(entity instanceof RefJavaFileImpl)) return null;
return RefJavaUtil.getInstance().getPackageName(entity);
}
@Override
public boolean belongsToScope(final PsiElement psiElement) {
return !(psiElement instanceof PsiTypeParameter);
}
@Override
public void export(@NotNull final RefEntity refEntity, @NotNull final Element element) {
if (refEntity instanceof RefElement) {
final SmartPsiElementPointer pointer = ((RefElement)refEntity).getPointer();
if (pointer != null) {
final PsiFile psiFile = pointer.getContainingFile();
if (psiFile instanceof PsiJavaFile) {
appendPackageElement(element, ((PsiJavaFile)psiFile).getPackageName());
}
}
}
}
@Override
public void onEntityInitialized(RefElement refElement, PsiElement psiElement) {
if (isEntryPoint(refElement)) {
getEntryPointsManager().addEntryPoint(refElement, false);
}
if (psiElement instanceof PsiClass) {
PsiClass psiClass = (PsiClass)psiElement;
EntryPointsManager entryPointsManager = getEntryPointsManager();
if (psiClass.isAnnotationType()){
entryPointsManager.addEntryPoint(refElement, false);
for (PsiMethod psiMethod : psiClass.getMethods()) {
entryPointsManager.addEntryPoint(myRefManager.getReference(psiMethod), false);
}
}
else if (psiClass.isEnum()) {
entryPointsManager.addEntryPoint(refElement, false);
}
}
}
private static void appendPackageElement(final Element element, final String packageName) {
final Element packageElement = new Element("package");
packageElement.addContent(packageName.isEmpty() ? InspectionsBundle.message("inspection.export.results.default") : packageName);
element.addContent(packageElement);
}
@Override
public EntryPointsManager getEntryPointsManager() {
if (myEntryPointsManager == null) {
final Project project = myRefManager.getProject();
myEntryPointsManager = EntryPointsManager.getInstance(project);
((EntryPointsManagerBase)myEntryPointsManager).addAllPersistentEntries(EntryPointsManagerBase.getInstance(project));
}
return myEntryPointsManager;
}
private class MyJavaElementVisitor extends JavaElementVisitor {
private final RefJavaUtil myRefUtil;
public MyJavaElementVisitor() {
myRefUtil = RefJavaUtil.getInstance();
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression);
}
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
}
@Override
public void visitReferenceParameterList(final PsiReferenceParameterList list) {
super.visitReferenceParameterList(list);
final PsiMember member = PsiTreeUtil.getParentOfType(list, PsiMember.class);
if (member instanceof PsiTypeParameter) {
final PsiMember owner = ((PsiTypeParameter)member).getOwner();
if (owner != null) {
for (PsiClassType type : ((PsiTypeParameter)member).getExtendsListTypes()) {
myRefUtil.addTypeReference(owner, type, myRefManager);
}
}
}
final PsiType[] typeArguments = list.getTypeArguments();
for (PsiType type : typeArguments) {
myRefUtil.addTypeReference(member, type, myRefManager);
}
}
@Override
public void visitClass(PsiClass aClass) {
if (!(aClass instanceof PsiTypeParameter)) {
super.visitClass(aClass);
RefElement refClass = myRefManager.getReference(aClass);
if (refClass != null) {
((RefClassImpl)refClass).buildReferences();
}
}
}
@Override
public void visitMethod(final PsiMethod method) {
super.visitMethod(method);
final RefElement refElement = myRefManager.getReference(method);
if (refElement instanceof RefMethodImpl) {
((RefMethodImpl)refElement).buildReferences();
}
}
@Override
public void visitField(final PsiField field) {
super.visitField(field);
final RefElement refElement = myRefManager.getReference(field);
if (refElement instanceof RefFieldImpl) {
((RefFieldImpl)refElement).buildReferences();
}
}
@Override
public void visitDocComment(PsiDocComment comment) {
super.visitDocComment(comment);
final PsiDocTag[] tags = comment.getTags();
for (PsiDocTag tag : tags) {
if (Comparing.strEqual(tag.getName(), SuppressionUtil.SUPPRESS_INSPECTIONS_TAG_NAME)) {
final PsiElement[] dataElements = tag.getDataElements();
if (dataElements != null && dataElements.length > 0) {
final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(comment, PsiModifierListOwner.class);
if (listOwner != null) {
final RefElementImpl element = (RefElementImpl)myRefManager.getReference(listOwner);
if (element != null) {
String suppression = "";
for (PsiElement dataElement : dataElements) {
suppression += "," + dataElement.getText();
}
element.addSuppression(suppression);
}
}
}
}
}
}
@Override
public void visitAnnotation(PsiAnnotation annotation) {
super.visitAnnotation(annotation);
if (Comparing.strEqual(annotation.getQualifiedName(), BatchSuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME)) {
final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(annotation, PsiModifierListOwner.class);
if (listOwner != null) {
final RefElementImpl element = (RefElementImpl)myRefManager.getReference(listOwner);
if (element != null) {
StringBuilder buf = new StringBuilder();
final PsiNameValuePair[] nameValuePairs = annotation.getParameterList().getAttributes();
for (PsiNameValuePair nameValuePair : nameValuePairs) {
buf.append(",").append(nameValuePair.getText().replaceAll("[{}\"\"]", ""));
}
if (buf.length() > 0) {
element.addSuppression(buf.substring(1));
}
}
}
}
}
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
myRefUtil.addTypeReference(variable, variable.getType(), myRefManager);
}
@Override
public void visitInstanceOfExpression(PsiInstanceOfExpression expression) {
super.visitInstanceOfExpression(expression);
final PsiTypeElement typeElement = expression.getCheckType();
if (typeElement != null) {
myRefUtil.addTypeReference(expression, typeElement.getType(), myRefManager);
}
}
@Override
public void visitThisExpression(PsiThisExpression expression) {
super.visitThisExpression(expression);
final PsiJavaCodeReferenceElement qualifier = expression.getQualifier();
if (qualifier != null) {
myRefUtil.addTypeReference(expression, expression.getType(), myRefManager);
RefClass ownerClass = myRefUtil.getOwnerClass(myRefManager, expression);
if (ownerClass != null) {
RefClassImpl refClass = (RefClassImpl)myRefManager.getReference(qualifier.resolve());
if (refClass != null) {
refClass.addInstanceReference(ownerClass);
}
}
}
}
}
}