| /* |
| * 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); |
| } |
| } |
| } |
| } |
| } |
| } |