mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-06 06:21:16 +07:00
942 lines
34 KiB
Java
942 lines
34 KiB
Java
/*
|
|
* Author: max
|
|
* Date: Oct 9, 2001
|
|
* Time: 8:43:17 PM
|
|
*/
|
|
|
|
package com.intellij.codeInspection.ex;
|
|
|
|
import com.intellij.analysis.AnalysisScope;
|
|
import com.intellij.codeEditor.printing.ExportToHTMLSettings;
|
|
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
|
|
import com.intellij.codeInspection.*;
|
|
import com.intellij.codeInspection.export.ExportToHTMLDialog;
|
|
import com.intellij.codeInspection.export.HTMLExportFrameMaker;
|
|
import com.intellij.codeInspection.reference.*;
|
|
import com.intellij.codeInspection.ui.InspectCodePanel;
|
|
import com.intellij.codeInspection.ui.InspectionResultsView;
|
|
import com.intellij.ide.BrowserUtil;
|
|
import com.intellij.ide.actions.NextOccurenceToolbarAction;
|
|
import com.intellij.ide.actions.PreviousOccurenceToolbarAction;
|
|
import com.intellij.ide.impl.ContentManagerWatcher;
|
|
import com.intellij.openapi.actionSystem.*;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.application.PathManager;
|
|
import com.intellij.openapi.components.ProjectComponent;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.openapi.help.HelpManager;
|
|
import com.intellij.openapi.module.Module;
|
|
import com.intellij.openapi.progress.ProcessCanceledException;
|
|
import com.intellij.openapi.progress.ProgressIndicator;
|
|
import com.intellij.openapi.progress.ProgressManager;
|
|
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.project.ex.ProjectEx;
|
|
import com.intellij.openapi.projectRoots.ProjectJdk;
|
|
import com.intellij.openapi.roots.ui.configuration.LibrariesEditor;
|
|
import com.intellij.openapi.ui.Messages;
|
|
import com.intellij.openapi.util.*;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.openapi.wm.ToolWindow;
|
|
import com.intellij.openapi.wm.ToolWindowAnchor;
|
|
import com.intellij.openapi.wm.ToolWindowId;
|
|
import com.intellij.openapi.wm.ToolWindowManager;
|
|
import com.intellij.peer.PeerFactory;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.javadoc.PsiDocComment;
|
|
import com.intellij.psi.javadoc.PsiDocTag;
|
|
import com.intellij.psi.search.*;
|
|
import com.intellij.psi.util.PsiTreeUtil;
|
|
import com.intellij.ui.AutoScrollToSourceHandler;
|
|
import com.intellij.ui.content.*;
|
|
import com.intellij.util.containers.HashMap;
|
|
import org.jdom.Document;
|
|
import org.jdom.Element;
|
|
|
|
import javax.swing.*;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.util.*;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
public class InspectionManagerEx extends InspectionManager implements JDOMExternalizable, ProjectComponent {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.InspectionManagerEx");
|
|
private RefManager myRefManager;
|
|
private ContentManager myContentManager;
|
|
private AnalysisScope myCurrentScope;
|
|
private final UIOptions myUIOptions;
|
|
private final Project myProject;
|
|
private List<JobDescriptor> myJobDescriptors;
|
|
private InspectionResultsView myView = null;
|
|
|
|
private HashMap<PsiElement, List<DerivedMethodsProcessor>> myDerivedMethodsRequests;
|
|
private HashMap<PsiElement, List<DerivedClassesProcessor>> myDerivedClassesRequests;
|
|
private HashMap<PsiElement, List<UsagesProcessor>> myMethodUsagesRequests;
|
|
private HashMap<PsiElement, List<UsagesProcessor>> myFieldUsagesRequests;
|
|
private HashMap<PsiElement, List<UsagesProcessor>> myClassUsagesRequests;
|
|
private ProgressIndicator myProgressIndicator;
|
|
private String myCurrentProfileName;
|
|
|
|
|
|
public static final JobDescriptor BUILD_GRAPH = new JobDescriptor("Processing project usages in ");
|
|
public static final JobDescriptor FIND_EXTERNAL_USAGES = new JobDescriptor("Processing external usages of ");
|
|
public static final JobDescriptor LOCAL_ANALYSIS = new JobDescriptor("Analyzing code in ");
|
|
|
|
public static final String HELP_ID = "codeInspection";
|
|
|
|
public static final String SUPPRESS_INSPECTIONS_TAG_NAME = "noinspection";
|
|
public static final String SUPPRESS_INSPECTIONS_ANNOTATION_NAME = "com.intellij.util.annotations.NoInspection";
|
|
|
|
//for use in local comments
|
|
public static final Pattern SUPPRESS_PATTERN = Pattern.compile("//" + SUPPRESS_INSPECTIONS_TAG_NAME + " (\\w+(,\\w+)*)");
|
|
|
|
private InspectionProfile myExternalProfile = null;
|
|
|
|
public InspectionManagerEx(Project project) {
|
|
myProject = project;
|
|
|
|
myUIOptions = new UIOptions();
|
|
|
|
myRefManager = null;
|
|
myCurrentScope = null;
|
|
myContentManager = null;
|
|
myCurrentProfileName = "Default";
|
|
}
|
|
|
|
public Project getProject() {
|
|
return myProject;
|
|
}
|
|
|
|
public ContentManager getContentManager() {
|
|
return myContentManager;
|
|
}
|
|
|
|
public void initComponent() { }
|
|
|
|
public void disposeComponent() {
|
|
cleanup();
|
|
}
|
|
|
|
public void projectOpened() {
|
|
myContentManager = PeerFactory.getInstance().getContentFactory().createContentManager(new ComponentContentUI(), true, myProject);
|
|
ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
|
|
ToolWindow toolWindow = toolWindowManager.registerToolWindow(ToolWindowId.INSPECTION,
|
|
myContentManager.getComponent(),
|
|
ToolWindowAnchor.BOTTOM);
|
|
toolWindow.setIcon(IconLoader.getIcon("/general/toolWindowInspection.png"));
|
|
new ContentManagerWatcher(toolWindow, myContentManager);
|
|
myContentManager.addContentManagerListener(new ContentManagerAdapter() {
|
|
public void contentRemoved(ContentManagerEvent event) {
|
|
myView = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
public void readExternal(Element element) throws InvalidDataException {
|
|
DefaultJDOMExternalizer.readExternal(myUIOptions, element);
|
|
|
|
Element profileElement = element.getChild("profile");
|
|
if (profileElement != null) {
|
|
myCurrentProfileName = profileElement.getAttributeValue("name");
|
|
}
|
|
else {
|
|
myCurrentProfileName = "Default";
|
|
}
|
|
}
|
|
|
|
public void writeExternal(Element element) throws WriteExternalException {
|
|
DefaultJDOMExternalizer.writeExternal(myUIOptions, element);
|
|
|
|
Element profileElement = new Element("profile");
|
|
profileElement.setAttribute("name", myCurrentProfileName);
|
|
element.addContent(profileElement);
|
|
}
|
|
|
|
public InspectionProfile getCurrentProfile() {
|
|
if (myExternalProfile != null) return myExternalProfile;
|
|
InspectionProfile profile = InspectionProfileManager.getInstance().getProfile(myCurrentProfileName);
|
|
if (profile == null) {
|
|
myCurrentProfileName = "Default";
|
|
profile = InspectionProfileManager.getInstance().getProfile(myCurrentProfileName);
|
|
}
|
|
return profile;
|
|
}
|
|
|
|
public ProblemDescriptor createProblemDescriptor(PsiElement psiElement,
|
|
String descriptionTemplate,
|
|
LocalQuickFix fix,
|
|
ProblemHighlightType highlightType) {
|
|
return new ProblemDescriptorImpl(psiElement, descriptionTemplate, fix, highlightType);
|
|
}
|
|
|
|
public void projectClosed() {
|
|
ToolWindowManager.getInstance(myProject).unregisterToolWindow(ToolWindowId.INSPECTION);
|
|
}
|
|
|
|
private void addView(InspectionResultsView view) {
|
|
myView = view;
|
|
ContentManager contentManager = getContentManager();
|
|
|
|
Content content = PeerFactory.getInstance().getContentFactory().createContent(view, "FOOO", false);
|
|
|
|
content.setDisplayName("Inspection Results");
|
|
contentManager.addContent(content);
|
|
contentManager.setSelectedContent(content);
|
|
|
|
ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.INSPECTION).activate(null);
|
|
}
|
|
|
|
private boolean isInspectionToolIdMentioned(String inspectionsList, String inspectionToolID) {
|
|
String[] ids = inspectionsList.split(",");
|
|
for (int i = 0; i < ids.length; i++) {
|
|
String id = ids[i];
|
|
if (id.equals(inspectionToolID) || id.equals("ALL")) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean isToCheckMember(PsiDocCommentOwner member, String inspectionToolID) {
|
|
PsiDocComment docComment = member.getDocComment();
|
|
if (docComment != null) {
|
|
PsiDocTag inspectionTag = docComment.findTagByName(SUPPRESS_INSPECTIONS_TAG_NAME);
|
|
if (inspectionTag != null) {
|
|
|
|
String valueText = inspectionTag.getValueElement().getText();
|
|
return !isInspectionToolIdMentioned(valueText, inspectionToolID);
|
|
}
|
|
}
|
|
|
|
PsiModifierList modifierList = member.getModifierList();
|
|
if (modifierList != null) {
|
|
PsiAnnotation annotation = modifierList.findAnnotation(SUPPRESS_INSPECTIONS_ANNOTATION_NAME);
|
|
if (annotation != null) {
|
|
PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
|
|
if (attributeValue instanceof PsiArrayInitializerMemberValue) {
|
|
PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue)attributeValue).getInitializers();
|
|
for (int i = 0; i < initializers.length; i++) {
|
|
PsiAnnotationMemberValue initializer = initializers[i];
|
|
if (initializer instanceof PsiLiteralExpression) {
|
|
Object value = ((PsiLiteralExpression)initializer).getValue();
|
|
if (inspectionToolID.equals(value) || "ALL".equals(value)) return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public boolean inspectionResultSuppressed(PsiElement place, LocalInspectionTool tool) {
|
|
PsiStatement statement = PsiTreeUtil.getParentOfType(place, PsiStatement.class);
|
|
if (statement != null) {
|
|
PsiElement prev = PsiTreeUtil.skipSiblingsBackward(statement, new Class[]{PsiWhiteSpace.class});
|
|
if (prev instanceof PsiComment) {
|
|
String text = prev.getText();
|
|
Matcher matcher = SUPPRESS_PATTERN.matcher(text);
|
|
if (matcher.matches()) {
|
|
return isInspectionToolIdMentioned(matcher.group(1), tool.getID());
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public interface DerivedClassesProcessor {
|
|
boolean process(PsiClass inheritor);
|
|
}
|
|
|
|
public interface DerivedMethodsProcessor {
|
|
boolean process(PsiMethod derivedMethod);
|
|
}
|
|
|
|
public interface UsagesProcessor {
|
|
boolean process(PsiReference psiReference);
|
|
}
|
|
|
|
public void enqueueClassUsagesProcessing(RefClass refClass, UsagesProcessor p) {
|
|
if (myClassUsagesRequests == null) myClassUsagesRequests = new HashMap<PsiElement, List<UsagesProcessor>>();
|
|
enqueueRequestImpl(refClass, myClassUsagesRequests, p);
|
|
}
|
|
|
|
public void enqueueDerivedClassesProcessing(RefClass refClass, DerivedClassesProcessor p) {
|
|
if (myDerivedClassesRequests == null) myDerivedClassesRequests = new HashMap<PsiElement, List<DerivedClassesProcessor>>();
|
|
enqueueRequestImpl(refClass, myDerivedClassesRequests, p);
|
|
}
|
|
|
|
public void enqueueDerivedMethodsProcessing(RefMethod refMethod, DerivedMethodsProcessor p) {
|
|
if (refMethod.isConstructor() || refMethod.isStatic()) return;
|
|
if (myDerivedMethodsRequests == null) myDerivedMethodsRequests = new HashMap<PsiElement, List<DerivedMethodsProcessor>>();
|
|
enqueueRequestImpl(refMethod, myDerivedMethodsRequests, p);
|
|
}
|
|
|
|
public void enqueueFieldUsagesProcessor(RefField refField, UsagesProcessor p) {
|
|
if (myFieldUsagesRequests == null) myFieldUsagesRequests = new HashMap<PsiElement, List<UsagesProcessor>>();
|
|
enqueueRequestImpl(refField, myFieldUsagesRequests, p);
|
|
}
|
|
|
|
public void enqueueMethodUsagesProcessor(RefMethod refMethod, UsagesProcessor p) {
|
|
if (myMethodUsagesRequests == null) myMethodUsagesRequests = new HashMap<PsiElement, List<UsagesProcessor>>();
|
|
enqueueRequestImpl(refMethod, myMethodUsagesRequests, p);
|
|
}
|
|
|
|
private static void enqueueRequestImpl(RefElement refElement, HashMap requestMap, Object processor) {
|
|
ArrayList requests = (ArrayList)requestMap.get(refElement.getElement());
|
|
if (requests == null) {
|
|
requests = new ArrayList();
|
|
requestMap.put(refElement.getElement(), requests);
|
|
}
|
|
requests.add(processor);
|
|
}
|
|
|
|
private void cleanup() {
|
|
myProgressIndicator = null;
|
|
|
|
myDerivedMethodsRequests = null;
|
|
myDerivedClassesRequests = null;
|
|
myMethodUsagesRequests = null;
|
|
myFieldUsagesRequests = null;
|
|
myClassUsagesRequests = null;
|
|
|
|
getCurrentProfile().cleanup();
|
|
|
|
EntryPointsManager.getInstance(getProject()).cleanup();
|
|
|
|
if (myRefManager != null) {
|
|
myRefManager.cleanup();
|
|
myRefManager = null;
|
|
}
|
|
}
|
|
|
|
public void setCurrentScope(AnalysisScope currentScope) {
|
|
myCurrentScope = currentScope;
|
|
}
|
|
|
|
public void doInspections(final AnalysisScope scope, boolean showDialog) {
|
|
while (PsiManager.getInstance(getProject()).findClass("java.lang.Object") == null) {
|
|
Messages.showMessageDialog(getProject(),
|
|
"The JDK is not configured properly for this project. Inspection cannot proceed.",
|
|
"Error",
|
|
Messages.getErrorIcon());
|
|
final ProjectJdk projectJdk = LibrariesEditor.chooseAndSetJDK(myProject);
|
|
if (projectJdk == null) return;
|
|
}
|
|
|
|
if (showDialog || myCurrentScope == null) {
|
|
final InspectCodePanel itc = new InspectCodePanel(this, scope);
|
|
|
|
itc.show();
|
|
if (!itc.isOK()) return;
|
|
|
|
|
|
}
|
|
|
|
close();
|
|
getContentManager().removeAllContents();
|
|
|
|
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
|
public void run() {
|
|
myCurrentScope = scope;
|
|
launchInspections(scope);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void rerun() {
|
|
if (myCurrentScope.isValid()) {
|
|
cleanup();
|
|
doInspections(myCurrentScope, false);
|
|
}
|
|
}
|
|
|
|
public RefManager getRefManager() {
|
|
if (myRefManager == null) {
|
|
myRefManager = new RefManager(myProject, myCurrentScope);
|
|
}
|
|
|
|
return myRefManager;
|
|
}
|
|
|
|
public void launchInspectionsOffline(final AnalysisScope scope, OutputStream outStream) {
|
|
cleanup();
|
|
|
|
myCurrentScope = scope;
|
|
final Element root = new Element("problems");
|
|
final Document doc = new Document(root);
|
|
|
|
|
|
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
|
public void run() {
|
|
performInspectionsWithProgress(scope);
|
|
|
|
InspectionTool[] tools = getCurrentProfile().getInspectionTools(myProject);
|
|
for (int i = 0; i < tools.length; i++) {
|
|
InspectionTool tool = tools[i];
|
|
if (getCurrentProfile().isToolEnabled(HighlightDisplayKey.find(tool.getShortName()))) {
|
|
tool.exportResults(root);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
try {
|
|
((ProjectEx)getProject()).getMacroReplacements().substitute(doc.getRootElement(), SystemInfo.isFileSystemCaseSensitive);
|
|
JDOMUtil.writeDocument(doc, outStream, "\n");
|
|
}
|
|
catch (IOException e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
|
|
public void processSearchRequests() {
|
|
final PsiSearchHelper helper = PsiManager.getInstance(getProject()).getSearchHelper();
|
|
final RefManager refManager = getRefManager();
|
|
final AnalysisScope scope = refManager.getScope();
|
|
|
|
final SearchScope searchScope = new GlobalSearchScope() {
|
|
public boolean contains(VirtualFile file) {
|
|
return !scope.contains(file);
|
|
}
|
|
|
|
public int compare(VirtualFile file1, VirtualFile file2) {
|
|
return 0;
|
|
}
|
|
|
|
public boolean isSearchInModuleContent(Module aModule) {
|
|
return true;
|
|
}
|
|
|
|
public boolean isSearchInLibraries() {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
final ProgressIndicator progress = myProgressIndicator == null ? null : new ProgressWrapper(myProgressIndicator);
|
|
ProgressManager.getInstance().runProcess(new Runnable() {
|
|
public void run() {
|
|
if (myDerivedClassesRequests != null) {
|
|
ArrayList sortedIDs = getSortedIDs(myDerivedClassesRequests);
|
|
for (int i = 0; i < sortedIDs.size(); i++) {
|
|
PsiClass psiClass = (PsiClass)sortedIDs.get(i);
|
|
incrementJobDoneAmount(FIND_EXTERNAL_USAGES, psiClass.getQualifiedName());
|
|
|
|
final List<DerivedClassesProcessor> processors = myDerivedClassesRequests.get(psiClass);
|
|
helper.processInheritors(new PsiBaseElementProcessor<PsiClass>() {
|
|
public boolean execute(PsiClass element) {
|
|
PsiClass inheritor = element;
|
|
if (scope.contains(inheritor)) return true;
|
|
DerivedClassesProcessor[] processorsArrayed = processors.toArray(new DerivedClassesProcessor[processors.size()]);
|
|
for (int j = 0; j < processorsArrayed.length; j++) {
|
|
DerivedClassesProcessor processor = processorsArrayed[j];
|
|
if (!processor.process(inheritor)) {
|
|
processors.remove(processor);
|
|
}
|
|
}
|
|
return processors.size() > 0;
|
|
}
|
|
}, psiClass, searchScope, false);
|
|
}
|
|
|
|
myDerivedClassesRequests = null;
|
|
}
|
|
|
|
if (myDerivedMethodsRequests != null) {
|
|
ArrayList sortedIDs = getSortedIDs(myDerivedMethodsRequests);
|
|
for (int i = 0; i < sortedIDs.size(); i++) {
|
|
PsiMethod psiMethod = (PsiMethod)sortedIDs.get(i);
|
|
final RefMethod refMethod = (RefMethod)refManager.getReference(psiMethod);
|
|
|
|
incrementJobDoneAmount(FIND_EXTERNAL_USAGES, RefUtil.getQualifiedName(refMethod));
|
|
|
|
final List<DerivedMethodsProcessor> processors = myDerivedMethodsRequests.get(psiMethod);
|
|
helper.processOverridingMethods(new PsiBaseElementProcessor<PsiMethod>() {
|
|
public boolean execute(PsiMethod element) {
|
|
PsiMethod derivedMethod = element;
|
|
if (scope.contains(derivedMethod)) return true;
|
|
DerivedMethodsProcessor[] processorsArrayed = processors.toArray(new DerivedMethodsProcessor[processors.size()]);
|
|
for (int j = 0; j < processorsArrayed.length; j++) {
|
|
DerivedMethodsProcessor processor = processorsArrayed[j];
|
|
if (!processor.process(derivedMethod)) {
|
|
processors.remove(processor);
|
|
}
|
|
}
|
|
|
|
return processors.size() > 0;
|
|
}
|
|
}, psiMethod, searchScope, true);
|
|
}
|
|
|
|
myDerivedMethodsRequests = null;
|
|
}
|
|
|
|
if (myFieldUsagesRequests != null) {
|
|
ArrayList sortedIDs = getSortedIDs(myFieldUsagesRequests);
|
|
for (int i = 0; i < sortedIDs.size(); i++) {
|
|
PsiField psiField = (PsiField)sortedIDs.get(i);
|
|
final List<UsagesProcessor> processors = myFieldUsagesRequests.get(psiField);
|
|
|
|
incrementJobDoneAmount(FIND_EXTERNAL_USAGES, RefUtil.getQualifiedName(refManager.getReference(psiField)));
|
|
|
|
helper.processReferences(createReferenceProcessor(processors), psiField, searchScope, false);
|
|
}
|
|
|
|
myFieldUsagesRequests = null;
|
|
}
|
|
|
|
if (myClassUsagesRequests != null) {
|
|
ArrayList sortedIDs = getSortedIDs(myClassUsagesRequests);
|
|
for (int i = 0; i < sortedIDs.size(); i++) {
|
|
PsiClass psiClass = (PsiClass)sortedIDs.get(i);
|
|
final List<UsagesProcessor> processors = myClassUsagesRequests.get(psiClass);
|
|
|
|
incrementJobDoneAmount(FIND_EXTERNAL_USAGES, psiClass.getQualifiedName());
|
|
|
|
helper.processReferences(createReferenceProcessor(processors), psiClass, searchScope, false);
|
|
}
|
|
|
|
myClassUsagesRequests = null;
|
|
}
|
|
|
|
if (myMethodUsagesRequests != null) {
|
|
ArrayList sortedIDs = getSortedIDs(myMethodUsagesRequests);
|
|
for (int i = 0; i < sortedIDs.size(); i++) {
|
|
PsiMethod psiMethod = (PsiMethod)sortedIDs.get(i);
|
|
final List<UsagesProcessor> processors = myMethodUsagesRequests.get(psiMethod);
|
|
|
|
incrementJobDoneAmount(FIND_EXTERNAL_USAGES, RefUtil.getQualifiedName(refManager.getReference(psiMethod)));
|
|
|
|
helper.processReferencesIncludingOverriding(createReferenceProcessor(processors), psiMethod, searchScope);
|
|
}
|
|
|
|
myMethodUsagesRequests = null;
|
|
}
|
|
}
|
|
}, progress);
|
|
}
|
|
|
|
private static class ProgressWrapper extends ProgressIndicatorBase {
|
|
private ProgressIndicator myOriginal;
|
|
|
|
public ProgressWrapper(final ProgressIndicator original) {
|
|
myOriginal = original;
|
|
}
|
|
|
|
public boolean isCanceled() {
|
|
return myOriginal.isCanceled();
|
|
}
|
|
}
|
|
|
|
private int getRequestCount() {
|
|
int sum = 0;
|
|
|
|
sum += getRequestListSize(myClassUsagesRequests);
|
|
sum += getRequestListSize(myDerivedClassesRequests);
|
|
sum += getRequestListSize(myDerivedMethodsRequests);
|
|
sum += getRequestListSize(myFieldUsagesRequests);
|
|
sum += getRequestListSize(myMethodUsagesRequests);
|
|
|
|
return sum;
|
|
}
|
|
|
|
private static int getRequestListSize(HashMap list) {
|
|
if (list == null) return 0;
|
|
return list.size();
|
|
}
|
|
|
|
private static ArrayList getSortedIDs(HashMap requests) {
|
|
ArrayList result = new ArrayList();
|
|
for (Iterator iterator = requests.keySet().iterator(); iterator.hasNext();) {
|
|
PsiElement id = (PsiElement)iterator.next();
|
|
result.add(id);
|
|
}
|
|
|
|
Collections.sort(result, new Comparator() {
|
|
public int compare(Object o1, Object o2) {
|
|
PsiElement i1 = (PsiElement)o1;
|
|
PsiElement i2 = (PsiElement)o2;
|
|
|
|
return i1.getContainingFile().getName().compareTo(i2.getContainingFile().getName());
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
private PsiReferenceProcessor createReferenceProcessor(final List<UsagesProcessor> processors) {
|
|
return new PsiReferenceProcessor() {
|
|
public boolean execute(PsiReference reference) {
|
|
AnalysisScope scope = getRefManager().getScope();
|
|
if (scope.contains(reference.getElement()) ||
|
|
PsiTreeUtil.getParentOfType(reference.getElement(), PsiDocComment.class) != null) {
|
|
return true;
|
|
}
|
|
UsagesProcessor[] processorsArrayed = processors.toArray(new UsagesProcessor[processors.size()]);
|
|
for (int j = 0; j < processorsArrayed.length; j++) {
|
|
UsagesProcessor processor = processorsArrayed[j];
|
|
if (!processor.process(reference)) {
|
|
processors.remove(processor);
|
|
}
|
|
}
|
|
|
|
return processors.size() > 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
private void launchInspections(final AnalysisScope scope) {
|
|
cleanup();
|
|
|
|
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
|
public void run() {
|
|
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
|
|
}
|
|
});
|
|
|
|
|
|
LOG.info("Code inspection started");
|
|
|
|
Runnable runInspection = new Runnable() {
|
|
public void run() {
|
|
performInspectionsWithProgress(scope);
|
|
}
|
|
};
|
|
|
|
if (!ApplicationManager.getApplication().runProcessWithProgressSynchronously(runInspection, "Inspecting Code...", true, myProject)) return;
|
|
|
|
InspectionResultsView view = new InspectionResultsView(myProject);
|
|
InspectionTool[] tools = getCurrentProfile().getInspectionTools(myProject);
|
|
if (!view.update(tools)) {
|
|
Messages.showMessageDialog(myProject,
|
|
"No suspicious code found",
|
|
"Code Inspection",
|
|
Messages.getInformationIcon());
|
|
}
|
|
else {
|
|
addView(view);
|
|
}
|
|
}
|
|
|
|
private void performInspectionsWithProgress(final AnalysisScope scope) {
|
|
try {
|
|
myProgressIndicator = ProgressManager.getInstance().getProgressIndicator();
|
|
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
|
public void run() {
|
|
try {
|
|
PsiManager.getInstance(myProject).startBatchFilesProcessingMode();
|
|
getRefManager().inspectionReadActionStarted();
|
|
EntryPointsManager.getInstance(getProject()).resolveEntryPoints(getRefManager());
|
|
|
|
InspectionTool[] tools = getCurrentProfile().getInspectionTools(myProject);
|
|
ArrayList<LocalInspectionToolWrapper> localTools = initJobDescriptors(tools, scope);
|
|
|
|
List<InspectionTool> needRepeatSearchRequest = new ArrayList<InspectionTool>();
|
|
runTools(tools, localTools, needRepeatSearchRequest, scope);
|
|
performPostRunFindUsages(needRepeatSearchRequest);
|
|
}
|
|
catch (ProcessCanceledException e) {
|
|
cleanup();
|
|
throw e;
|
|
}
|
|
finally {
|
|
PsiManager.getInstance(myProject).finishBatchFilesProcessingMode();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
finally {
|
|
if (myRefManager != null) {
|
|
getRefManager().inspectionReadActionFinished();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void performPostRunFindUsages(List<InspectionTool> needRepeatSearchRequest) {
|
|
FIND_EXTERNAL_USAGES.setTotalAmount(getRequestCount() * 2);
|
|
|
|
do {
|
|
processSearchRequests();
|
|
InspectionTool[] requestors = needRepeatSearchRequest.toArray(new InspectionTool[needRepeatSearchRequest.size()]);
|
|
for (int i = 0; i < requestors.length; i++) {
|
|
InspectionTool requestor = requestors[i];
|
|
if (!requestor.queryExternalUsagesRequests()) needRepeatSearchRequest.remove(requestor);
|
|
}
|
|
int oldSearchRequestCount = FIND_EXTERNAL_USAGES.getTotalAmount();
|
|
float proportion = FIND_EXTERNAL_USAGES.getProgress();
|
|
int totalAmount = oldSearchRequestCount + getRequestCount() * 2;
|
|
FIND_EXTERNAL_USAGES.setTotalAmount(totalAmount);
|
|
FIND_EXTERNAL_USAGES.setDoneAmount((int)(totalAmount * proportion));
|
|
}
|
|
while (needRepeatSearchRequest.size() > 0);
|
|
}
|
|
|
|
private void runTools(InspectionTool[] tools,
|
|
final ArrayList<LocalInspectionToolWrapper> localTools,
|
|
List<InspectionTool> needRepeatSearchRequest,
|
|
final AnalysisScope scope) {
|
|
final PsiManager psiManager = PsiManager.getInstance(myProject);
|
|
for (int i = 0; i < tools.length; i++) {
|
|
InspectionTool tool = tools[i];
|
|
if (getCurrentProfile().isToolEnabled(HighlightDisplayKey.find(tool.getShortName()))) tool.initialize(this);
|
|
}
|
|
|
|
try {
|
|
scope.accept(new PsiRecursiveElementVisitor() {
|
|
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
|
}
|
|
|
|
public void visitJavaFile(PsiJavaFile file) {
|
|
incrementJobDoneAmount(LOCAL_ANALYSIS, file.getVirtualFile().getPresentableUrl());
|
|
for (int i = 0; i < localTools.size(); i++) {
|
|
LocalInspectionToolWrapper tool = localTools.get(i);
|
|
tool.processFile(file);
|
|
psiManager.dropResolveCaches();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
catch (ProcessCanceledException e) {
|
|
throw e;
|
|
}
|
|
catch (Throwable e) {
|
|
LOG.error(e);
|
|
}
|
|
|
|
for (int i = 0; i < tools.length; i++) {
|
|
InspectionTool tool = tools[i];
|
|
if (getCurrentProfile().isToolEnabled(HighlightDisplayKey.find(tool.getShortName())) &&
|
|
!(tool instanceof LocalInspectionToolWrapper)) {
|
|
try {
|
|
tool.runInspection(scope);
|
|
|
|
if (tool.queryExternalUsagesRequests()) {
|
|
needRepeatSearchRequest.add(tool);
|
|
}
|
|
}
|
|
catch (ProcessCanceledException e) {
|
|
throw e;
|
|
}
|
|
catch (Throwable e) {
|
|
LOG.error(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private ArrayList<LocalInspectionToolWrapper> initJobDescriptors(InspectionTool[] tools,
|
|
final AnalysisScope scope) {
|
|
ArrayList<LocalInspectionToolWrapper> localTools = new ArrayList<LocalInspectionToolWrapper>();
|
|
myJobDescriptors = new ArrayList<JobDescriptor>();
|
|
for (int i = 0; i < tools.length; i++) {
|
|
InspectionTool tool = tools[i];
|
|
if (getCurrentProfile().isToolEnabled(HighlightDisplayKey.find(tool.getShortName()))) {
|
|
if (tool instanceof LocalInspectionToolWrapper) {
|
|
LocalInspectionToolWrapper wrapper = (LocalInspectionToolWrapper)tool;
|
|
localTools.add(wrapper);
|
|
appendJobDescriptor(LOCAL_ANALYSIS);
|
|
}
|
|
else {
|
|
JobDescriptor[] jobDescriptors = tool.getJobDescriptors();
|
|
for (int j = 0; j < jobDescriptors.length; j++) {
|
|
appendJobDescriptor(jobDescriptors[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BUILD_GRAPH.setTotalAmount(scope.getFileCount());
|
|
LOCAL_ANALYSIS.setTotalAmount(scope.getFileCount());
|
|
return localTools;
|
|
}
|
|
|
|
private void appendJobDescriptor(JobDescriptor job) {
|
|
if (!myJobDescriptors.contains(job)) {
|
|
myJobDescriptors.add(job);
|
|
job.setDoneAmount(0);
|
|
}
|
|
}
|
|
|
|
public UIOptions getUIOptions() {
|
|
return myUIOptions;
|
|
}
|
|
|
|
public class UIOptions implements JDOMExternalizable {
|
|
public boolean AUTOSCROLL_TO_SOURCE = false;
|
|
public float SPLITTER_PROPORTION = 0.5f;
|
|
public final AutoScrollToSourceHandler myAutoScrollToSourceHandler;
|
|
|
|
public UIOptions() {
|
|
myAutoScrollToSourceHandler = new AutoScrollToSourceHandler(myProject) {
|
|
protected boolean isAutoScrollMode() {
|
|
return AUTOSCROLL_TO_SOURCE;
|
|
}
|
|
|
|
protected void setAutoScrollMode(boolean state) {
|
|
AUTOSCROLL_TO_SOURCE = state;
|
|
}
|
|
};
|
|
}
|
|
|
|
public void readExternal(org.jdom.Element element) throws InvalidDataException {
|
|
DefaultJDOMExternalizer.readExternal(this, element);
|
|
}
|
|
|
|
public void writeExternal(org.jdom.Element element) throws WriteExternalException {
|
|
DefaultJDOMExternalizer.writeExternal(this, element);
|
|
}
|
|
}
|
|
|
|
private class CloseAction extends AnAction {
|
|
private CloseAction() {
|
|
super("Close", null, IconLoader.getIcon("/actions/cancel.png"));
|
|
}
|
|
|
|
public void actionPerformed(AnActionEvent e) {
|
|
close();
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
getContentManager().removeAllContents();
|
|
cleanup();
|
|
}
|
|
|
|
public void addCommonActions(DefaultActionGroup group, InspectionResultsView view) {
|
|
group.add(new CloseAction());
|
|
group.add(createToggleAutoscrollAction());
|
|
group.add(new RerunAction(view));
|
|
group.add(new PreviousOccurenceToolbarAction(view.getOccurenceNavigator()));
|
|
group.add(new NextOccurenceToolbarAction(view.getOccurenceNavigator()));
|
|
group.add(new ExportHTMLAction());
|
|
group.add(new HelpAction());
|
|
}
|
|
|
|
public ToggleAction createToggleAutoscrollAction() {
|
|
return myUIOptions.myAutoScrollToSourceHandler.createToggleAction();
|
|
}
|
|
|
|
public void installAutoscrollHandler(JTree tree) {
|
|
myUIOptions.myAutoScrollToSourceHandler.install(tree);
|
|
}
|
|
|
|
private void exportHTML() {
|
|
ExportToHTMLDialog exportToHTMLDialog = new ExportToHTMLDialog(myProject);
|
|
final ExportToHTMLSettings exportToHTMLSettings = ExportToHTMLSettings.getInstance(myProject);
|
|
if (exportToHTMLSettings.OUTPUT_DIRECTORY == null) {
|
|
exportToHTMLSettings.OUTPUT_DIRECTORY = PathManager.getHomePath() + File.separator + "exportToHTML";
|
|
}
|
|
exportToHTMLDialog.reset();
|
|
exportToHTMLDialog.show();
|
|
if (!exportToHTMLDialog.isOK()) {
|
|
return;
|
|
}
|
|
exportToHTMLDialog.apply();
|
|
|
|
final String outputDirectoryName = exportToHTMLSettings.OUTPUT_DIRECTORY;
|
|
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
|
public void run() {
|
|
final Runnable exportRunnable = new Runnable() {
|
|
public void run() {
|
|
HTMLExportFrameMaker maker = new HTMLExportFrameMaker(outputDirectoryName, myProject);
|
|
maker.start();
|
|
try {
|
|
myView.exportHTML(maker);
|
|
}
|
|
catch (ProcessCanceledException e) {
|
|
// Do nothing here.
|
|
}
|
|
|
|
maker.done();
|
|
}
|
|
};
|
|
|
|
if (!ApplicationManager.getApplication().runProcessWithProgressSynchronously(exportRunnable, "Generating HTML...", true, myProject)) return;
|
|
|
|
if (exportToHTMLSettings.OPEN_IN_BROWSER) {
|
|
BrowserUtil.launchBrowser(exportToHTMLSettings.OUTPUT_DIRECTORY + File.separator + "index.html");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void setLeftSplitterProportion(float proportion) {
|
|
getUIOptions().SPLITTER_PROPORTION = proportion;
|
|
}
|
|
|
|
private static class HelpAction extends AnAction {
|
|
private HelpAction() {
|
|
super("Help", null, IconLoader.getIcon("/actions/help.png"));
|
|
}
|
|
|
|
public void actionPerformed(AnActionEvent event) {
|
|
HelpManager.getInstance().invokeHelp(HELP_ID);
|
|
}
|
|
}
|
|
|
|
private class ExportHTMLAction extends AnAction {
|
|
public ExportHTMLAction() {
|
|
super("Export HTML", null, IconLoader.getIcon("/actions/export.png"));
|
|
}
|
|
|
|
public void actionPerformed(AnActionEvent e) {
|
|
exportHTML();
|
|
}
|
|
}
|
|
|
|
private class RerunAction extends AnAction {
|
|
public RerunAction(JComponent comp) {
|
|
super("Rerun Inspection", "Rerun Inspection", IconLoader.getIcon("/actions/refreshUsages.png"));
|
|
registerCustomShortcutSet(CommonShortcuts.getRerun(), comp);
|
|
}
|
|
|
|
public void update(AnActionEvent e) {
|
|
e.getPresentation().setEnabled(myCurrentScope.isValid());
|
|
}
|
|
|
|
public void actionPerformed(AnActionEvent e) {
|
|
rerun();
|
|
}
|
|
}
|
|
|
|
public String getComponentName() {
|
|
return "InspectionManager";
|
|
}
|
|
|
|
public void refreshViews() {
|
|
InspectionTool[] tools = getCurrentProfile().getInspectionTools(myProject);
|
|
myView.update(tools);
|
|
}
|
|
|
|
public void incrementJobDoneAmount(JobDescriptor job, String message) {
|
|
if (myProgressIndicator == null) return;
|
|
|
|
ProgressManager.getInstance().checkCanceled();
|
|
|
|
int old = job.getDoneAmount();
|
|
job.setDoneAmount(old + 1);
|
|
|
|
int jobCount = myJobDescriptors.size();
|
|
float totalProgress = 0;
|
|
for (int i = 0; i < myJobDescriptors.size(); i++) {
|
|
totalProgress += myJobDescriptors.get(i).getProgress();
|
|
}
|
|
|
|
totalProgress /= jobCount;
|
|
|
|
myProgressIndicator.setFraction(totalProgress);
|
|
myProgressIndicator.setText(job.getDisplayName() + " " + message);
|
|
}
|
|
|
|
public void setProfile(InspectionProfile profile) {
|
|
myCurrentProfileName = profile.getName();
|
|
}
|
|
|
|
public void setExternalProfile(InspectionProfile profile) {
|
|
myExternalProfile = profile;
|
|
}
|
|
|
|
public boolean areResultsShown() {
|
|
return myView != null;
|
|
}
|
|
}
|