2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.codeInspection.ex;
19 import com.intellij.analysis.AnalysisScope;
20 import com.intellij.analysis.AnalysisUIOptions;
21 import com.intellij.analysis.PerformAnalysisInBackgroundOption;
22 import com.intellij.codeInsight.FileModificationService;
23 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
24 import com.intellij.codeInsight.daemon.impl.HighlightInfoProcessor;
25 import com.intellij.codeInsight.daemon.impl.LocalInspectionsPass;
26 import com.intellij.codeInspection.*;
27 import com.intellij.codeInspection.lang.GlobalInspectionContextExtension;
28 import com.intellij.codeInspection.reference.RefElement;
29 import com.intellij.codeInspection.reference.RefEntity;
30 import com.intellij.codeInspection.reference.RefManagerImpl;
31 import com.intellij.codeInspection.reference.RefVisitor;
32 import com.intellij.codeInspection.ui.DefaultInspectionToolPresentation;
33 import com.intellij.codeInspection.ui.InspectionResultsView;
34 import com.intellij.codeInspection.ui.InspectionToolPresentation;
35 import com.intellij.concurrency.JobLauncher;
36 import com.intellij.concurrency.JobLauncherImpl;
37 import com.intellij.concurrency.SensitiveProgressWrapper;
38 import com.intellij.lang.annotation.ProblemGroup;
39 import com.intellij.lang.injection.InjectedLanguageManager;
40 import com.intellij.notification.NotificationGroup;
41 import com.intellij.openapi.Disposable;
42 import com.intellij.openapi.actionSystem.ToggleAction;
43 import com.intellij.openapi.application.ApplicationManager;
44 import com.intellij.openapi.application.ex.ApplicationManagerEx;
45 import com.intellij.openapi.command.CommandProcessor;
46 import com.intellij.openapi.components.PathMacroManager;
47 import com.intellij.openapi.diagnostic.Logger;
48 import com.intellij.openapi.editor.Document;
49 import com.intellij.openapi.progress.*;
50 import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
51 import com.intellij.openapi.project.IndexNotReadyException;
52 import com.intellij.openapi.project.Project;
53 import com.intellij.openapi.project.ProjectCoreUtil;
54 import com.intellij.openapi.project.ProjectUtilCore;
55 import com.intellij.openapi.roots.FileIndex;
56 import com.intellij.openapi.roots.ProjectRootManager;
57 import com.intellij.openapi.ui.MessageType;
58 import com.intellij.openapi.util.*;
59 import com.intellij.openapi.util.io.FileUtil;
60 import com.intellij.openapi.util.text.StringUtil;
61 import com.intellij.openapi.vfs.CharsetToolkit;
62 import com.intellij.openapi.vfs.VirtualFile;
63 import com.intellij.openapi.wm.ToolWindowId;
64 import com.intellij.openapi.wm.ToolWindowManager;
65 import com.intellij.psi.*;
66 import com.intellij.psi.search.LocalSearchScope;
67 import com.intellij.psi.search.SearchScope;
68 import com.intellij.psi.util.PsiTreeUtil;
69 import com.intellij.psi.util.PsiUtilCore;
70 import com.intellij.ui.content.*;
71 import com.intellij.util.*;
72 import com.intellij.util.containers.ContainerUtil;
73 import com.intellij.util.containers.HashMap;
74 import com.intellij.util.ui.UIUtil;
75 import gnu.trove.THashSet;
76 import org.jdom.Element;
77 import org.jetbrains.annotations.NonNls;
78 import org.jetbrains.annotations.NotNull;
79 import org.jetbrains.annotations.Nullable;
82 import java.io.FileOutputStream;
83 import java.io.IOException;
84 import java.io.OutputStreamWriter;
85 import java.lang.reflect.Constructor;
87 import java.util.concurrent.ArrayBlockingQueue;
88 import java.util.concurrent.BlockingQueue;
89 import java.util.concurrent.ConcurrentMap;
90 import java.util.concurrent.LinkedBlockingQueue;
92 public class GlobalInspectionContextImpl extends GlobalInspectionContextBase implements GlobalInspectionContext {
93 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl");
94 private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("Inspection Results", ToolWindowId.INSPECTION);
95 private final NotNullLazyValue<ContentManager> myContentManager;
96 private InspectionResultsView myView;
97 private Content myContent;
100 private AnalysisUIOptions myUIOptions;
102 public GlobalInspectionContextImpl(@NotNull Project project, @NotNull NotNullLazyValue<ContentManager> contentManager) {
105 myUIOptions = AnalysisUIOptions.getInstance(project).copy();
106 myContentManager = contentManager;
110 private ContentManager getContentManager() {
111 return myContentManager.getValue();
114 public synchronized void addView(@NotNull InspectionResultsView view, @NotNull String title) {
115 if (myContent != null) return;
116 myContentManager.getValue().addContentManagerListener(new ContentManagerAdapter() {
118 public void contentRemoved(ContentManagerEvent event) {
119 if (event.getContent() == myContent) {
120 if (myView != null) {
129 myContent = ContentFactory.SERVICE.getInstance().createContent(view, title, false);
131 myContent.setDisposer(myView);
133 ContentManager contentManager = getContentManager();
134 contentManager.addContent(myContent);
135 contentManager.setSelectedContent(myContent);
137 ToolWindowManager.getInstance(getProject()).getToolWindow(ToolWindowId.INSPECTION).activate(null);
140 public void addView(@NotNull InspectionResultsView view) {
141 addView(view, view.getCurrentProfileName() == null
142 ? InspectionsBundle.message("inspection.results.title")
143 : InspectionsBundle.message("inspection.results.for.profile.toolwindow.title", view.getCurrentProfileName()));
148 public void doInspections(@NotNull final AnalysisScope scope) {
149 if (myContent != null) {
150 getContentManager().removeContent(myContent, true);
152 super.doInspections(scope);
155 public void launchInspectionsOffline(@NotNull final AnalysisScope scope,
156 @Nullable final String outputPath,
157 final boolean runGlobalToolsOnly,
158 @NotNull final List<File> inspectionsResults) {
159 performInspectionsWithProgressAndExportResults(scope, runGlobalToolsOnly, true, outputPath, inspectionsResults);
162 public void performInspectionsWithProgressAndExportResults(@NotNull final AnalysisScope scope,
163 final boolean runGlobalToolsOnly,
164 final boolean isOfflineInspections,
165 @Nullable final String outputPath,
166 @NotNull final List<File> inspectionsResults) {
168 setCurrentScope(scope);
170 final Runnable action = new Runnable() {
173 DefaultInspectionToolPresentation.setOutputPath(outputPath);
175 performInspectionsWithProgress(scope, runGlobalToolsOnly, isOfflineInspections);
176 exportResults(inspectionsResults, outputPath);
179 DefaultInspectionToolPresentation.setOutputPath(null);
183 if (isOfflineInspections) {
184 ApplicationManager.getApplication().runReadAction(action);
191 private void exportResults(@NotNull List<File> inspectionsResults, @Nullable String outputPath) {
192 @NonNls final String ext = ".xml";
193 final Map<Element, Tools> globalTools = new HashMap<Element, Tools>();
194 for (Map.Entry<String,Tools> entry : myTools.entrySet()) {
195 final Tools sameTools = entry.getValue();
196 boolean hasProblems = false;
197 String toolName = entry.getKey();
198 if (sameTools != null) {
199 for (ScopeToolState toolDescr : sameTools.getTools()) {
200 InspectionToolWrapper toolWrapper = toolDescr.getTool();
201 if (toolWrapper instanceof LocalInspectionToolWrapper) {
202 hasProblems = new File(outputPath, toolName + ext).exists();
205 InspectionToolPresentation presentation = getPresentation(toolWrapper);
206 presentation.updateContent();
207 if (presentation.hasReportedProblems()) {
208 final Element root = new Element(InspectionsBundle.message("inspection.problems"));
209 globalTools.put(root, sameTools);
210 LOG.assertTrue(!hasProblems, toolName);
218 new File(outputPath).mkdirs();
219 final File file = new File(outputPath, toolName + ext);
220 inspectionsResults.add(file);
222 .writeToFile(file, ("</" + InspectionsBundle.message("inspection.problems") + ">").getBytes(CharsetToolkit.UTF8_CHARSET), true);
224 catch (IOException e) {
230 getRefManager().iterate(new RefVisitor() {
232 public void visitElement(@NotNull final RefEntity refEntity) {
233 for (Map.Entry<Element, Tools> entry : globalTools.entrySet()) {
234 Tools tools = entry.getValue();
235 Element element = entry.getKey();
236 for (ScopeToolState state : tools.getTools()) {
238 InspectionToolWrapper toolWrapper = state.getTool();
239 InspectionToolPresentation presentation = getPresentation(toolWrapper);
240 presentation.exportResults(element, refEntity);
242 catch (Throwable e) {
243 LOG.error("Problem when exporting: " + refEntity.getExternalName(), e);
250 for (Map.Entry<Element, Tools> entry : globalTools.entrySet()) {
251 final String toolName = entry.getValue().getShortName();
252 Element element = entry.getKey();
253 element.setAttribute(LOCAL_TOOL_ATTRIBUTE, Boolean.toString(false));
254 final org.jdom.Document doc = new org.jdom.Document(element);
255 PathMacroManager.getInstance(getProject()).collapsePaths(doc.getRootElement());
257 new File(outputPath).mkdirs();
258 final File file = new File(outputPath, toolName + ext);
259 inspectionsResults.add(file);
261 OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), CharsetToolkit.UTF8_CHARSET);
263 JDOMUtil.writeDocument(doc, writer, "\n");
269 catch (IOException e) {
275 public void ignoreElement(@NotNull InspectionProfileEntry tool, @NotNull PsiElement element) {
276 final RefElement refElement = getRefManager().getReference(element);
277 final Tools tools = myTools.get(tool.getShortName());
279 for (ScopeToolState state : tools.getTools()) {
280 InspectionToolWrapper toolWrapper = state.getTool();
281 ignoreElementRecursively(toolWrapper, refElement);
286 public InspectionResultsView getView() {
290 private void ignoreElementRecursively(@NotNull InspectionToolWrapper toolWrapper, final RefEntity refElement) {
291 if (refElement != null) {
292 InspectionToolPresentation presentation = getPresentation(toolWrapper);
293 presentation.ignoreCurrentElement(refElement);
294 final List<RefEntity> children = refElement.getChildren();
295 if (children != null) {
296 for (RefEntity child : children) {
297 ignoreElementRecursively(toolWrapper, child);
304 public AnalysisUIOptions getUIOptions() {
308 public void setSplitterProportion(final float proportion) {
309 myUIOptions.SPLITTER_PROPORTION = proportion;
313 public ToggleAction createToggleAutoscrollAction() {
314 return myUIOptions.getAutoScrollToSourceHandler().createToggleAction();
318 protected void launchInspections(@NotNull final AnalysisScope scope) {
319 myUIOptions = AnalysisUIOptions.getInstance(getProject()).copy();
320 myView = new InspectionResultsView(getProject(), getCurrentProfile(), scope, this, new InspectionRVContentProviderImpl(getProject()));
321 super.launchInspections(scope);
326 protected PerformInBackgroundOption createOption() {
327 return new PerformAnalysisInBackgroundOption(getProject());
331 protected void notifyInspectionsFinished() {
332 if (ApplicationManager.getApplication().isUnitTestMode()) return;
333 UIUtil.invokeLaterIfNeeded(new Runnable() {
336 LOG.info("Code inspection finished");
338 if (myView != null) {
339 if (!myView.update() && !getUIOptions().SHOW_ONLY_DIFF) {
340 NOTIFICATION_GROUP.createNotification(InspectionsBundle.message("inspection.no.problems.message"), MessageType.INFO).notify(getProject());
352 protected void runTools(@NotNull final AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) {
353 final ProgressIndicator progressIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
354 if (progressIndicator == null) {
355 throw new IncorrectOperationException("Must be run under progress");
357 if (!isOfflineInspections && ApplicationManager.getApplication().isDispatchThread()) {
358 throw new IncorrectOperationException("Must not start inspections from within EDT");
360 if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
361 throw new IncorrectOperationException("Must not start inspections from within write action");
363 // in offline inspection application we don't care about global read action
364 if (!isOfflineInspections && ApplicationManager.getApplication().isReadAccessAllowed()) {
365 throw new IncorrectOperationException("Must not start inspections from within global read action");
367 final InspectionManager inspectionManager = InspectionManager.getInstance(getProject());
368 final List<Tools> globalTools = new ArrayList<Tools>();
369 final List<Tools> localTools = new ArrayList<Tools>();
370 final List<Tools> globalSimpleTools = new ArrayList<Tools>();
371 initializeTools(globalTools, localTools, globalSimpleTools);
372 appendPairedInspectionsForUnfairTools(globalTools, globalSimpleTools, localTools);
374 ((RefManagerImpl)getRefManager()).initializeAnnotators();
375 runGlobalTools(scope, inspectionManager, globalTools, isOfflineInspections);
377 if (runGlobalToolsOnly) return;
379 final Set<VirtualFile> localScopeFiles = scope.toSearchScope() instanceof LocalSearchScope ? new THashSet<VirtualFile>() : null;
380 for (Tools tools : globalSimpleTools) {
381 GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
382 GlobalSimpleInspectionTool tool = (GlobalSimpleInspectionTool)toolWrapper.getTool();
383 tool.inspectionStarted(inspectionManager, this, getPresentation(toolWrapper));
386 final boolean headlessEnvironment = ApplicationManager.getApplication().isHeadlessEnvironment();
387 final Map<String, InspectionToolWrapper> map = getInspectionWrappersMap(localTools);
389 final BlockingQueue<PsiFile> filesToInspect = new ArrayBlockingQueue<PsiFile>(1000);
390 final Queue<PsiFile> filesFailedToInspect = new LinkedBlockingQueue<PsiFile>();
391 // use original progress indicator here since we don't want it to cancel on write action start
392 startIterateScope(scope, localScopeFiles, headlessEnvironment, filesToInspect, progressIndicator);
394 Processor<PsiFile> processor = new Processor<PsiFile>() {
396 public boolean process(final PsiFile file) {
397 ProgressManager.checkCanceled();
398 if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(new Runnable() {
401 if (!file.isValid()) {
404 inspectFile(file, inspectionManager, localTools, globalSimpleTools, map);
407 throw new ProcessCanceledException();
414 Disposable disposable = Disposer.newDisposable();
415 ProgressIndicator wrapper = new SensitiveProgressWrapper(progressIndicator);
417 ProgressIndicatorUtils.forceWriteActionPriority(wrapper, disposable);
420 // use wrapper here to cancel early when write action start but do not affect the original indicator
421 ((JobLauncherImpl)JobLauncher.getInstance()).processQueue(filesToInspect, filesFailedToInspect, wrapper, TOMBSTONE, processor);
424 catch (ProcessCanceledException ignored) {
425 progressIndicator.checkCanceled();
426 // PCE may be thrown from inside wrapper when write action started
427 // go on with the write and then resume processing the rest of the queue
428 assert !ApplicationManager.getApplication().isReadAccessAllowed();
429 assert !ApplicationManager.getApplication().isDispatchThread();
431 // wait for write action to complete
432 ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance());
435 Disposer.dispose(disposable);
438 progressIndicator.checkCanceled();
440 for (Tools tools : globalSimpleTools) {
441 GlobalInspectionToolWrapper toolWrapper = (GlobalInspectionToolWrapper)tools.getTool();
442 GlobalSimpleInspectionTool tool = (GlobalSimpleInspectionTool)toolWrapper.getTool();
443 ProblemDescriptionsProcessor problemDescriptionProcessor = getProblemDescriptionProcessor(toolWrapper, map);
444 tool.inspectionFinished(inspectionManager, this, problemDescriptionProcessor);
448 private boolean inspectFile(@NotNull final PsiFile file,
449 @NotNull final InspectionManager inspectionManager,
450 @NotNull List<Tools> localTools,
451 @NotNull List<Tools> globalSimpleTools,
452 @NotNull final Map<String, InspectionToolWrapper> wrappersMap) {
453 Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
454 if (document == null) return true;
456 VirtualFile virtualFile = file.getVirtualFile();
457 String url = ProjectUtilCore.displayUrlRelativeToProject(virtualFile, virtualFile.getPresentableUrl(), getProject(), true, false);
458 incrementJobDoneAmount(getStdJobDescriptors().LOCAL_ANALYSIS, url);
460 final LocalInspectionsPass pass = new LocalInspectionsPass(file, document, 0,
461 file.getTextLength(), LocalInspectionsPass.EMPTY_PRIORITY_RANGE, true,
462 HighlightInfoProcessor.getEmpty());
464 final List<LocalInspectionToolWrapper> lTools = getWrappersFromTools(localTools, file);
465 pass.doInspectInBatch(this, inspectionManager, lTools);
467 final List<GlobalInspectionToolWrapper> tools = getWrappersFromTools(globalSimpleTools, file);
468 JobLauncher.getInstance().invokeConcurrentlyUnderProgress(tools, myProgressIndicator, false, new Processor<GlobalInspectionToolWrapper>() {
470 public boolean process(GlobalInspectionToolWrapper toolWrapper) {
471 GlobalSimpleInspectionTool tool = (GlobalSimpleInspectionTool)toolWrapper.getTool();
472 ProblemsHolder holder = new ProblemsHolder(inspectionManager, file, false);
473 ProblemDescriptionsProcessor problemDescriptionProcessor = getProblemDescriptionProcessor(toolWrapper, wrappersMap);
474 tool.checkFile(file, inspectionManager, holder, GlobalInspectionContextImpl.this, problemDescriptionProcessor);
475 InspectionToolPresentation toolPresentation = getPresentation(toolWrapper);
476 LocalDescriptorsUtil.addProblemDescriptors(holder.getResults(), false, GlobalInspectionContextImpl.this, null,
477 CONVERT, toolPresentation);
482 catch (ProcessCanceledException e) {
483 final Throwable cause = e.getCause();
487 LOG.error("In file: " + file, cause);
489 catch (IndexNotReadyException e) {
492 catch (Throwable e) {
493 LOG.error("In file: " + file.getName(), e);
496 InjectedLanguageManager.getInstance(getProject()).dropFileCaches(file);
501 private static final PsiFile TOMBSTONE = PsiUtilCore.NULL_PSI_FILE;
503 private void startIterateScope(@NotNull final AnalysisScope scope,
504 @Nullable final Collection<VirtualFile> localScopeFiles,
505 final boolean headlessEnvironment,
506 @NotNull final BlockingQueue<PsiFile> outFilesToInspect,
507 @NotNull final ProgressIndicator progressIndicator) {
508 ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
512 final FileIndex fileIndex = ProjectRootManager.getInstance(getProject()).getFileIndex();
513 scope.accept(new Processor<VirtualFile>() {
515 public boolean process(final VirtualFile file) {
516 progressIndicator.checkCanceled();
517 if (ProjectCoreUtil.isProjectOrWorkspaceFile(file) || !fileIndex.isInContent(file)) return true;
518 final PsiFile[] psiFile = new PsiFile[1];
520 Document document = ApplicationManager.getApplication().runReadAction(new Computable<Document>() {
522 public Document compute() {
523 if (getProject().isDisposed()) throw new ProcessCanceledException();
524 PsiFile psi = PsiManager.getInstance(getProject()).findFile(file);
525 Document document = psi == null ? null : shouldProcess(psi, headlessEnvironment, localScopeFiles);
526 if (document != null) {
532 //do not inspect binary files
533 if (document != null && psiFile[0] != null) {
535 LOG.assertTrue(!ApplicationManager.getApplication().isReadAccessAllowed());
536 outFilesToInspect.put(psiFile[0]);
538 catch (InterruptedException e) {
546 catch (ProcessCanceledException e) {
547 // ignore, but put tombstone
551 outFilesToInspect.put(TOMBSTONE);
553 catch (InterruptedException e) {
561 private Document shouldProcess(@NotNull PsiFile file, boolean headlessEnvironment, @Nullable Collection<VirtualFile> localScopeFiles) {
562 final VirtualFile virtualFile = file.getVirtualFile();
563 if (virtualFile == null) return null;
564 if (isBinary(file)) return null; //do not inspect binary files
566 if (myView == null && !headlessEnvironment) {
567 throw new ProcessCanceledException();
570 if (LOG.isDebugEnabled()) {
571 LOG.debug("Running local inspections on " + virtualFile.getPath());
574 if (SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile)) return null;
575 if (localScopeFiles != null && !localScopeFiles.add(virtualFile)) return null;
577 return PsiDocumentManager.getInstance(getProject()).getDocument(file);
580 private void runGlobalTools(@NotNull final AnalysisScope scope,
581 @NotNull final InspectionManager inspectionManager,
582 @NotNull List<Tools> globalTools,
583 boolean isOfflineInspections) {
584 LOG.assertTrue(!ApplicationManager.getApplication().isReadAccessAllowed() || isOfflineInspections, "Must not run under read action, too unresponsive");
585 final List<InspectionToolWrapper> needRepeatSearchRequest = new ArrayList<InspectionToolWrapper>();
587 final boolean canBeExternalUsages = scope.getScopeType() != AnalysisScope.PROJECT;
588 for (Tools tools : globalTools) {
589 for (ScopeToolState state : tools.getTools()) {
590 final InspectionToolWrapper toolWrapper = state.getTool();
591 final GlobalInspectionTool tool = (GlobalInspectionTool)toolWrapper.getTool();
592 final InspectionToolPresentation toolPresentation = getPresentation(toolWrapper);
594 if (tool.isGraphNeeded()) {
596 ((RefManagerImpl)getRefManager()).findAllDeclarations();
598 catch (Throwable e) {
599 getStdJobDescriptors().BUILD_GRAPH.setDoneAmount(0);
603 ApplicationManager.getApplication().runReadAction(new Runnable() {
606 tool.runInspection(scope, inspectionManager, GlobalInspectionContextImpl.this, toolPresentation);
607 //skip phase when we are sure that scope already contains everything
608 if (canBeExternalUsages &&
609 tool.queryExternalUsagesRequests(inspectionManager, GlobalInspectionContextImpl.this, toolPresentation)) {
610 needRepeatSearchRequest.add(toolWrapper);
615 catch (ProcessCanceledException e) {
618 catch (IndexNotReadyException e) {
621 catch (Throwable e) {
626 for (GlobalInspectionContextExtension extension : myExtensions.values()) {
628 extension.performPostRunActivities(needRepeatSearchRequest, this);
630 catch (ProcessCanceledException e) {
633 catch (IndexNotReadyException e) {
636 catch (Throwable e) {
642 private void appendPairedInspectionsForUnfairTools(@NotNull List<Tools> globalTools,
643 @NotNull List<Tools> globalSimpleTools,
644 @NotNull List<Tools> localTools) {
645 Tools[] larray = localTools.toArray(new Tools[localTools.size()]);
646 for (Tools tool : larray) {
647 LocalInspectionToolWrapper toolWrapper = (LocalInspectionToolWrapper)tool.getTool();
648 LocalInspectionTool localTool = toolWrapper.getTool();
649 if (localTool instanceof PairedUnfairLocalInspectionTool) {
650 String batchShortName = ((PairedUnfairLocalInspectionTool)localTool).getInspectionForBatchShortName();
651 InspectionProfile currentProfile = getCurrentProfile();
652 InspectionToolWrapper batchInspection;
653 if (currentProfile == null) {
654 batchInspection = null;
657 final InspectionToolWrapper pairedWrapper = currentProfile.getInspectionTool(batchShortName, getProject());
658 batchInspection = pairedWrapper != null ? pairedWrapper.createCopy() : null;
660 if (batchInspection != null && !myTools.containsKey(batchShortName)) {
661 // add to existing inspections to run
662 InspectionProfileEntry batchTool = batchInspection.getTool();
663 Tools newTool = new ToolsImpl(batchInspection, batchInspection.getDefaultLevel(), true, true);
664 if (batchTool instanceof LocalInspectionTool) localTools.add(newTool);
665 else if (batchTool instanceof GlobalSimpleInspectionTool) globalSimpleTools.add(newTool);
666 else if (batchTool instanceof GlobalInspectionTool) globalTools.add(newTool);
667 else throw new AssertionError(batchTool);
668 myTools.put(batchShortName, newTool);
669 batchInspection.initialize(this);
676 private static <T extends InspectionToolWrapper> List<T> getWrappersFromTools(@NotNull List<Tools> localTools, @NotNull PsiFile file) {
677 final List<T> lTools = new ArrayList<T>();
678 for (Tools tool : localTools) {
679 //noinspection unchecked
680 final T enabledTool = (T)tool.getEnabledTool(file);
681 if (enabledTool != null) {
682 lTools.add(enabledTool);
689 private ProblemDescriptionsProcessor getProblemDescriptionProcessor(@NotNull final GlobalInspectionToolWrapper toolWrapper,
690 @NotNull final Map<String, InspectionToolWrapper> wrappersMap) {
691 return new ProblemDescriptionsProcessor() {
694 public CommonProblemDescriptor[] getDescriptions(@NotNull RefEntity refEntity) {
695 return new CommonProblemDescriptor[0];
699 public void ignoreElement(@NotNull RefEntity refEntity) {
704 public void addProblemElement(@Nullable RefEntity refEntity, @NotNull CommonProblemDescriptor... commonProblemDescriptors) {
705 for (CommonProblemDescriptor problemDescriptor : commonProblemDescriptors) {
706 if (!(problemDescriptor instanceof ProblemDescriptor)) {
709 ProblemGroup problemGroup = ((ProblemDescriptor)problemDescriptor).getProblemGroup();
711 InspectionToolWrapper targetWrapper = problemGroup == null ? toolWrapper : wrappersMap.get(problemGroup.getProblemName());
712 if (targetWrapper != null) { // Else it's switched off
713 InspectionToolPresentation toolPresentation = getPresentation(targetWrapper);
714 toolPresentation.addProblemElement(refEntity, problemDescriptor);
720 public RefEntity getElement(@NotNull CommonProblemDescriptor descriptor) {
727 private static Map<String, InspectionToolWrapper> getInspectionWrappersMap(@NotNull List<Tools> tools) {
728 Map<String, InspectionToolWrapper> name2Inspection = new HashMap<String, InspectionToolWrapper>(tools.size());
729 for (Tools tool : tools) {
730 InspectionToolWrapper toolWrapper = tool.getTool();
731 name2Inspection.put(toolWrapper.getShortName(), toolWrapper);
734 return name2Inspection;
737 private static final TripleFunction<LocalInspectionTool,PsiElement,GlobalInspectionContext,RefElement> CONVERT =
738 new TripleFunction<LocalInspectionTool, PsiElement, GlobalInspectionContext, RefElement>() {
740 public RefElement fun(LocalInspectionTool tool,
742 GlobalInspectionContext context) {
743 final PsiNamedElement problemElement = PsiTreeUtil.getNonStrictParentOfType(elt, PsiFile.class);
745 RefElement refElement = context.getRefManager().getReference(problemElement);
746 if (refElement == null && problemElement != null) { // no need to lose collected results
747 refElement = GlobalInspectionContextUtil.retrieveRefElement(elt, context);
755 public void close(boolean noSuspisiousCodeFound) {
756 if (!noSuspisiousCodeFound && (myView == null || myView.isRerun())) return;
757 AnalysisUIOptions.getInstance(getProject()).save(myUIOptions);
758 if (myContent != null) {
759 final ContentManager contentManager = getContentManager();
760 contentManager.removeContent(myContent, true);
763 super.close(noSuspisiousCodeFound);
767 public void cleanup() {
768 ((InspectionManagerEx)InspectionManager.getInstance(getProject())).closeRunningContext(this);
769 for (Tools tools : myTools.values()) {
770 for (ScopeToolState state : tools.getTools()) {
771 InspectionToolWrapper toolWrapper = state.getTool();
772 getPresentation(toolWrapper).finalCleanup();
778 public void refreshViews() {
779 if (myView != null) {
780 myView.updateView(false);
784 private final ConcurrentMap<InspectionToolWrapper, InspectionToolPresentation> myPresentationMap = ContainerUtil.newConcurrentMap();
786 public InspectionToolPresentation getPresentation(@NotNull InspectionToolWrapper toolWrapper) {
787 InspectionToolPresentation presentation = myPresentationMap.get(toolWrapper);
788 if (presentation == null) {
789 String presentationClass = StringUtil.notNullize(toolWrapper.myEP == null ? null : toolWrapper.myEP.presentation, DefaultInspectionToolPresentation.class.getName());
792 Constructor<?> constructor = Class.forName(presentationClass).getConstructor(InspectionToolWrapper.class, GlobalInspectionContextImpl.class);
793 presentation = (InspectionToolPresentation)constructor.newInstance(toolWrapper, this);
795 catch (Exception e) {
797 throw new RuntimeException(e);
799 presentation = ConcurrencyUtil.cacheOrGet(myPresentationMap, toolWrapper, presentation);
805 public void codeCleanup(@NotNull final Project project,
806 @NotNull final AnalysisScope scope,
807 @NotNull final InspectionProfile profile,
808 @Nullable final String commandName,
809 @Nullable final Runnable postRunnable,
810 final boolean modal) {
811 Task task = modal ? new Task.Modal(project, "Inspect code...", true) {
813 public void run(@NotNull ProgressIndicator indicator) {
814 cleanup(scope, profile, project, postRunnable, commandName);
816 } : new Task.Backgroundable(project, "Inspect code...", true) {
818 public void run(@NotNull ProgressIndicator indicator) {
819 cleanup(scope, profile, project, postRunnable, commandName);
822 ProgressManager.getInstance().run(task);
825 private void cleanup(@NotNull AnalysisScope scope,
826 @NotNull InspectionProfile profile,
827 @NotNull final Project project,
828 @Nullable final Runnable postRunnable,
829 @Nullable final String commandName) {
830 final int fileCount = scope.getFileCount();
831 final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
832 final List<LocalInspectionToolWrapper> lTools = new ArrayList<LocalInspectionToolWrapper>();
834 final LinkedHashMap<PsiFile, List<HighlightInfo>> results = new LinkedHashMap<PsiFile, List<HighlightInfo>>();
836 final SearchScope searchScope = scope.toSearchScope();
837 final TextRange range;
838 if (searchScope instanceof LocalSearchScope) {
839 final PsiElement[] elements = ((LocalSearchScope)searchScope).getScope();
840 range = elements.length == 1 ? ApplicationManager.getApplication().runReadAction(new Computable<TextRange>() {
842 public TextRange compute() {
843 return elements[0].getTextRange();
850 final Iterable<Tools> inspectionTools = ContainerUtil.filter(profile.getAllEnabledInspectionTools(project), new Condition<Tools>() {
852 public boolean value(Tools tools) {
853 assert tools != null;
854 return tools.getTool().getTool() instanceof CleanupLocalInspectionTool;
857 scope.accept(new PsiElementVisitor() {
860 public void visitFile(PsiFile file) {
861 if (progressIndicator != null) {
862 progressIndicator.setFraction(((double)++ myCount)/fileCount);
864 if (isBinary(file)) return;
865 for (final Tools tools : inspectionTools) {
866 final InspectionToolWrapper tool = tools.getEnabledTool(file);
867 if (tool instanceof LocalInspectionToolWrapper) {
868 lTools.add((LocalInspectionToolWrapper)tool);
869 tool.initialize(GlobalInspectionContextImpl.this);
873 if (!lTools.isEmpty()) {
874 final LocalInspectionsPass pass = new LocalInspectionsPass(file, PsiDocumentManager.getInstance(project).getDocument(file), range != null ? range.getStartOffset() : 0,
875 range != null ? range.getEndOffset() : file.getTextLength(), LocalInspectionsPass.EMPTY_PRIORITY_RANGE, true,
876 HighlightInfoProcessor.getEmpty());
877 Runnable runnable = new Runnable() {
880 pass.doInspectInBatch(GlobalInspectionContextImpl.this, InspectionManager.getInstance(project), lTools);
883 ApplicationManager.getApplication().runReadAction(runnable);
884 final List<HighlightInfo> infos = pass.getInfos();
885 if (searchScope instanceof LocalSearchScope) {
886 for (Iterator<HighlightInfo> iterator = infos.iterator(); iterator.hasNext(); ) {
887 final HighlightInfo info = iterator.next();
888 final TextRange infoRange = new TextRange(info.getStartOffset(), info.getEndOffset());
889 if (!((LocalSearchScope)searchScope).containsRange(file, infoRange)) {
894 if (!infos.isEmpty()) {
895 results.put(file, infos);
901 if (results.isEmpty()) {
902 UIUtil.invokeLaterIfNeeded(new Runnable() {
905 if (commandName != null) {
906 NOTIFICATION_GROUP.createNotification(InspectionsBundle.message("inspection.no.problems.message"), MessageType.INFO).notify(getProject());
908 if (postRunnable != null) {
915 Runnable runnable = new Runnable() {
918 if (!FileModificationService.getInstance().preparePsiElementsForWrite(results.keySet())) return;
920 final SequentialModalProgressTask progressTask = new SequentialModalProgressTask(project, "Code Cleanup", true);
921 progressTask.setMinIterationTime(200);
922 progressTask.setTask(new SequentialCleanupTask(project, results, progressTask));
923 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
926 if (commandName != null) {
927 CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
929 ApplicationManager.getApplication().runWriteAction(new Runnable() {
932 ProgressManager.getInstance().run(progressTask);
935 if (postRunnable != null) {
936 ApplicationManager.getApplication().invokeLater(postRunnable);
939 }, commandName, null);
942 if (ApplicationManager.getApplication().isDispatchThread()) {
945 ApplicationManager.getApplication().invokeLater(runnable);
949 private static boolean isBinary(@NotNull PsiFile file) {
950 return file instanceof PsiBinaryFile || file.getFileType().isBinary();