2 * Copyright 2000-2009 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
.daemon
.impl
.LocalInspectionsPass
;
23 import com
.intellij
.codeInspection
.*;
24 import com
.intellij
.codeInspection
.lang
.GlobalInspectionContextExtension
;
25 import com
.intellij
.codeInspection
.lang
.InspectionExtensionsFactory
;
26 import com
.intellij
.codeInspection
.reference
.*;
27 import com
.intellij
.codeInspection
.ui
.InspectionResultsView
;
28 import com
.intellij
.openapi
.actionSystem
.ToggleAction
;
29 import com
.intellij
.openapi
.application
.ApplicationManager
;
30 import com
.intellij
.openapi
.components
.PathMacroManager
;
31 import com
.intellij
.openapi
.diagnostic
.Logger
;
32 import com
.intellij
.openapi
.extensions
.Extensions
;
33 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
34 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
35 import com
.intellij
.openapi
.progress
.ProgressManager
;
36 import com
.intellij
.openapi
.progress
.Task
;
37 import com
.intellij
.openapi
.progress
.impl
.ProgressManagerImpl
;
38 import com
.intellij
.openapi
.progress
.util
.ProgressWrapper
;
39 import com
.intellij
.openapi
.project
.Project
;
40 import com
.intellij
.openapi
.project
.ProjectUtil
;
41 import com
.intellij
.openapi
.ui
.Messages
;
42 import com
.intellij
.openapi
.util
.Computable
;
43 import com
.intellij
.openapi
.util
.JDOMUtil
;
44 import com
.intellij
.openapi
.util
.Key
;
45 import com
.intellij
.openapi
.util
.NotNullLazyValue
;
46 import com
.intellij
.openapi
.vfs
.VirtualFile
;
47 import com
.intellij
.openapi
.wm
.ToolWindowId
;
48 import com
.intellij
.openapi
.wm
.ToolWindowManager
;
49 import com
.intellij
.profile
.Profile
;
50 import com
.intellij
.profile
.codeInspection
.InspectionProfileManager
;
51 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
52 import com
.intellij
.psi
.*;
53 import com
.intellij
.psi
.search
.scope
.packageSet
.NamedScope
;
54 import com
.intellij
.ui
.content
.*;
55 import com
.intellij
.util
.containers
.HashMap
;
56 import gnu
.trove
.THashMap
;
57 import org
.jdom
.Document
;
58 import org
.jdom
.Element
;
59 import org
.jetbrains
.annotations
.NonNls
;
60 import org
.jetbrains
.annotations
.NotNull
;
64 import java
.util
.ArrayList
;
65 import java
.util
.List
;
68 public class GlobalInspectionContextImpl
implements GlobalInspectionContext
{
69 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl");
71 private RefManager myRefManager
;
72 private final NotNullLazyValue
<ContentManager
> myContentManager
;
74 private AnalysisScope myCurrentScope
;
75 private final Project myProject
;
76 private List
<JobDescriptor
> myJobDescriptors
;
77 private InspectionResultsView myView
= null;
79 private Content myContent
= null;
82 private ProgressIndicator myProgressIndicator
;
83 public static final JobDescriptor BUILD_GRAPH
= new JobDescriptor(InspectionsBundle
.message("inspection.processing.job.descriptor"));
84 public static final JobDescriptor FIND_EXTERNAL_USAGES
=
85 new JobDescriptor(InspectionsBundle
.message("inspection.processing.job.descriptor1"));
88 private static final JobDescriptor LOCAL_ANALYSIS
= new JobDescriptor(InspectionsBundle
.message("inspection.processing.job.descriptor2"));
90 private InspectionProfile myExternalProfile
= null;
92 private final Map
<Key
, GlobalInspectionContextExtension
> myExtensions
= new HashMap
<Key
, GlobalInspectionContextExtension
>();
93 private boolean RUN_GLOBAL_TOOLS_ONLY
= false;
95 private final Map
<String
, Tools
> myTools
= new THashMap
<String
, Tools
>();
97 private final AnalysisUIOptions myUIOptions
;
99 public GlobalInspectionContextImpl(Project project
, NotNullLazyValue
<ContentManager
> contentManager
) {
102 myUIOptions
= AnalysisUIOptions
.getInstance(myProject
).copy();
104 myCurrentScope
= null;
105 myContentManager
= contentManager
;
106 for (InspectionExtensionsFactory factory
: Extensions
.getExtensions(InspectionExtensionsFactory
.EP_NAME
)) {
107 final GlobalInspectionContextExtension extension
= factory
.createGlobalInspectionContextExtension();
108 myExtensions
.put(extension
.getID(), extension
);
113 public Project
getProject() {
117 public <T
> T
getExtension(final Key
<T
> key
) {
118 return (T
)myExtensions
.get(key
);
121 public ContentManager
getContentManager() {
122 return myContentManager
.getValue();
125 public InspectionProfile
getCurrentProfile() {
126 if (myExternalProfile
!= null) return myExternalProfile
;
127 InspectionManagerEx managerEx
= (InspectionManagerEx
)InspectionManager
.getInstance(myProject
);
128 final InspectionProjectProfileManager inspectionProfileManager
= InspectionProjectProfileManager
.getInstance(myProject
);
129 Profile profile
= inspectionProfileManager
.getProfile(managerEx
.getCurrentProfile(), false);
130 if (profile
== null) {
131 profile
= InspectionProfileManager
.getInstance().getProfile(managerEx
.getCurrentProfile());
132 if (profile
!= null) return (InspectionProfile
)profile
;
134 final String
[] avaliableProfileNames
= inspectionProfileManager
.getAvailableProfileNames();
135 if (avaliableProfileNames
== null || avaliableProfileNames
.length
== 0) {
139 profile
= inspectionProfileManager
.getProfile(avaliableProfileNames
[0]);
141 return (InspectionProfile
)profile
;
144 public boolean isSuppressed(RefEntity entity
, String id
) {
145 return entity
instanceof RefElementImpl
&& ((RefElementImpl
)entity
).isSuppressed(id
);
148 public boolean shouldCheck(RefEntity entity
, GlobalInspectionTool tool
) {
149 if (entity
instanceof RefElementImpl
) {
150 final RefElementImpl refElement
= (RefElementImpl
)entity
;
151 if (refElement
.isSuppressed(tool
.getShortName())) return false;
153 final PsiFile file
= refElement
.getContainingFile();
155 if (file
== null) return false;
157 final Tools tools
= myTools
.get(tool
.getShortName());
158 for (ScopeToolState state
: tools
.getTools()) {
159 final NamedScope namedScope
= state
.getScope();
160 if (namedScope
== null || namedScope
.getValue().contains(file
, getCurrentProfile().getProfileManager().getScopesManager())) {
161 return state
.isEnabled() && ((GlobalInspectionToolWrapper
)state
.getTool()).getTool() == tool
;
169 public boolean isSuppressed(PsiElement element
, String id
) {
170 final RefManagerImpl refManager
= (RefManagerImpl
)getRefManager();
171 if (refManager
.isDeclarationsFound()) {
172 final RefElement refElement
= refManager
.getReference(element
);
173 return refElement
instanceof RefElementImpl
&& ((RefElementImpl
)refElement
).isSuppressed(id
);
175 return InspectionManagerEx
.isSuppressed(element
, id
);
179 public void addView(InspectionResultsView view
, String title
) {
180 myContentManager
.getValue().addContentManagerListener(new ContentManagerAdapter() {
181 public void contentRemoved(ContentManagerEvent event
) {
182 if (event
.getContent() == myContent
){
183 if (myView
!= null) {
192 ContentManager contentManager
= getContentManager();
193 myContent
= ContentFactory
.SERVICE
.getInstance().createContent(view
, title
, false);
195 myContent
.setDisposer(myView
);
196 contentManager
.addContent(myContent
);
197 contentManager
.setSelectedContent(myContent
);
199 ToolWindowManager
.getInstance(myProject
).getToolWindow(ToolWindowId
.INSPECTION
).activate(null);
202 private void addView(InspectionResultsView view
) {
203 addView(view
, view
.getCurrentProfileName() == null
204 ? InspectionsBundle
.message("inspection.results.title")
205 : InspectionsBundle
.message("inspection.results.for.profile.toolwindow.title", view
.getCurrentProfileName()));
209 private void cleanup() {
210 myProgressIndicator
= null;
212 for (GlobalInspectionContextExtension extension
: myExtensions
.values()) {
216 for (Tools tools
: myTools
.values()) {
217 for (ScopeToolState state
: tools
.getTools()) {
218 ((InspectionTool
)state
.getTool()).cleanup();
223 //EntryPointsManager.getInstance(getProject()).cleanup();
225 if (myRefManager
!= null) {
226 ((RefManagerImpl
)myRefManager
).cleanup();
228 if (myCurrentScope
!= null){
229 myCurrentScope
.invalidate();
230 myCurrentScope
= null;
235 public void setCurrentScope(AnalysisScope currentScope
) {
236 myCurrentScope
= currentScope
;
239 public void doInspections(final AnalysisScope scope
, final InspectionManager manager
) {
240 if (!InspectionManagerEx
.canRunInspections(myProject
, true)) return;
243 if (myContent
!= null) {
244 getContentManager().removeContent(myContent
, true);
247 ApplicationManager
.getApplication().invokeLater(new Runnable() {
249 myCurrentScope
= scope
;
250 launchInspections(scope
, manager
);
257 public RefManager
getRefManager() {
258 if (myRefManager
== null) {
259 myRefManager
= ApplicationManager
.getApplication().runReadAction(new Computable
<RefManagerImpl
>() {
260 public RefManagerImpl
compute() {
261 return new RefManagerImpl(myProject
, myCurrentScope
, GlobalInspectionContextImpl
.this);
268 public void launchInspectionsOffline(final AnalysisScope scope
,
269 final String outputPath
,
270 final boolean runWithEditorSettings
,
271 final boolean runGlobalToolsOnly
,
272 final InspectionManager manager
) {
275 myCurrentScope
= scope
;
277 InspectionTool
.setOutputPath(outputPath
);
278 final boolean oldToolsSettings
= RUN_GLOBAL_TOOLS_ONLY
;
279 RUN_GLOBAL_TOOLS_ONLY
= runGlobalToolsOnly
;
281 ApplicationManager
.getApplication().runReadAction(new Runnable() {
283 performInspectionsWithProgress(scope
, manager
);
284 @NonNls final String ext
= ".xml";
285 for (Map
.Entry
<String
,Tools
> stringSetEntry
: myTools
.entrySet()) {
286 final Element root
= new Element(InspectionsBundle
.message("inspection.problems"));
287 final Document doc
= new Document(root
);
288 final Tools sameTools
= stringSetEntry
.getValue();
289 boolean hasProblems
= false;
290 boolean isLocalTool
= false;
291 String toolName
= stringSetEntry
.getKey();
292 if (sameTools
!= null) {
293 for (ScopeToolState toolDescr
: sameTools
.getTools()) {
294 final InspectionTool tool
= (InspectionTool
)toolDescr
.getTool();
295 if (tool
instanceof LocalInspectionToolWrapper
) {
296 hasProblems
= new File(outputPath
, toolName
+ ext
).exists();
300 tool
.updateContent();
301 if (tool
.hasReportedProblems()) {
303 tool
.exportResults(root
);
308 if (!hasProblems
) continue;
309 @NonNls final String isLocalToolAttribute
= "is_local_tool";
310 root
.setAttribute(isLocalToolAttribute
, String
.valueOf(isLocalTool
));
311 OutputStream outStream
= null;
313 new File(outputPath
).mkdirs();
314 final File file
= new File(outputPath
, toolName
+ ext
);
316 outStream
= new BufferedOutputStream(new FileOutputStream(file
, true));
317 outStream
.write(("</" + InspectionsBundle
.message("inspection.problems") + ">").getBytes());
320 PathMacroManager
.getInstance(getProject()).collapsePaths(doc
.getRootElement());
321 outStream
= new BufferedOutputStream(new FileOutputStream(file
));
322 JDOMUtil
.writeDocument(doc
, outStream
, "\n");
325 catch (IOException e
) {
329 if (outStream
!= null) {
333 catch (IOException e
) {
344 InspectionTool
.setOutputPath(null);
345 RUN_GLOBAL_TOOLS_ONLY
= oldToolsSettings
;
350 public boolean isToCheckMember(@NotNull RefElement owner
, InspectionTool tool
) {
351 final PsiElement element
= owner
.getElement();
352 return isToCheckMember(element
, tool
) && !((RefElementImpl
)owner
).isSuppressed(tool
.getShortName());
355 public boolean isToCheckMember(final PsiElement element
, final InspectionTool tool
) {
357 final Tools tools
= myTools
.get(tool
.getShortName());
358 for (ScopeToolState state
: tools
.getTools()) {
359 final NamedScope namedScope
= state
.getScope();
360 if (namedScope
== null || namedScope
.getValue().contains(element
.getContainingFile(), getCurrentProfile().getProfileManager().getScopesManager())) {
361 return state
.isEnabled() && state
.getTool() == tool
;
369 public void ignoreElement(final InspectionTool tool
, final PsiElement element
) {
370 final RefElement refElement
= getRefManager().getReference(element
);
371 final Tools tools
= myTools
.get(tool
.getShortName());
373 for (ScopeToolState state
: tools
.getTools()) {
374 ignoreElementRecursively((InspectionTool
)state
.getTool(), refElement
);
379 private static void ignoreElementRecursively(final InspectionTool tool
, final RefEntity refElement
) {
380 if (refElement
!= null) {
381 tool
.ignoreCurrentElement(refElement
);
382 final List
<RefEntity
> children
= refElement
.getChildren();
383 if (children
!= null) {
384 for (RefEntity child
: children
) {
385 ignoreElementRecursively(tool
, child
);
391 public AnalysisUIOptions
getUIOptions() {
395 public void setSplitterProportion(final float proportion
) {
396 myUIOptions
.SPLITTER_PROPORTION
= proportion
;
399 public ToggleAction
createToggleAutoscrollAction() {
400 return myUIOptions
.getAutoScrollToSourceHandler().createToggleAction();
403 private void launchInspections(final AnalysisScope scope
, final InspectionManager manager
) {
404 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
406 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
410 LOG
.info("Code inspection started");
412 ProgressManager
.getInstance().run(new Task
.Backgroundable(getProject(), InspectionsBundle
.message("inspection.progress.title"), true, new PerformAnalysisInBackgroundOption(myProject
)) {
413 public void run(@NotNull ProgressIndicator indicator
) {
414 performInspectionsWithProgress(scope
, manager
);
418 public void onSuccess() {
419 SwingUtilities
.invokeLater(new Runnable() {
421 LOG
.info("Code inspection finished");
423 final InspectionResultsView view
= new InspectionResultsView(myProject
, getCurrentProfile(),
424 scope
, GlobalInspectionContextImpl
.this,
425 new InspectionRVContentProviderImpl(myProject
));
426 if (!view
.update() && !getUIOptions().SHOW_ONLY_DIFF
) {
427 Messages
.showMessageDialog(myProject
, InspectionsBundle
.message("inspection.no.problems.message"),
428 InspectionsBundle
.message("inspection.no.problems.dialog.title"), Messages
.getInformationIcon());
440 private void performInspectionsWithProgress(final AnalysisScope scope
, final InspectionManager manager
) {
441 final PsiManager psiManager
= PsiManager
.getInstance(myProject
);
442 myProgressIndicator
= ProgressManager
.getInstance().getProgressIndicator();
443 //init manager in read action
444 RefManagerImpl refManager
= (RefManagerImpl
)getRefManager();
446 psiManager
.startBatchFilesProcessingMode();
447 refManager
.inspectionReadActionStarted();
448 BUILD_GRAPH
.setTotalAmount(scope
.getFileCount());
449 LOCAL_ANALYSIS
.setTotalAmount(scope
.getFileCount());
450 final List
<InspectionProfileEntry
> needRepeatSearchRequest
= new ArrayList
<InspectionProfileEntry
>();
451 ((ProgressManagerImpl
)ProgressManager
.getInstance())
452 .executeProcessUnderProgress(new Runnable() { //to override current progress in order to hide useless messages/%
454 runTools(needRepeatSearchRequest
, scope
, manager
);
456 }, ProgressWrapper
.wrap(myProgressIndicator
));
458 catch (ProcessCanceledException e
) {
459 cleanup((InspectionManagerEx
)manager
);
462 catch (Exception e
) {
466 refManager
.inspectionReadActionFinished();
467 psiManager
.finishBatchFilesProcessingMode();
471 private void runTools(final List
<InspectionProfileEntry
> needRepeatSearchRequest
, final AnalysisScope scope
, final InspectionManager manager
) {
472 final List
<Tools
> usedTools
= new ArrayList
<Tools
>();
473 final List
<Tools
> localTools
= new ArrayList
<Tools
>();
474 initializeTools(usedTools
, localTools
);
475 ((RefManagerImpl
)getRefManager()).initializeAnnotators();
476 for (Tools tools
: usedTools
) {
477 for (ScopeToolState state
: tools
.getTools()) {
478 final InspectionTool tool
= (InspectionTool
)state
.getTool();
480 if (tool
.isGraphNeeded()) {
481 ((RefManagerImpl
)tool
.getRefManager()).findAllDeclarations();
483 tool
.runInspection(scope
, manager
);
484 if (tool
.queryExternalUsagesRequests(manager
)) {
485 needRepeatSearchRequest
.add(tool
);
488 catch (ProcessCanceledException e
) {
491 catch (Exception e
) {
496 for (GlobalInspectionContextExtension extension
: myExtensions
.values()) {
498 extension
.performPostRunActivities(needRepeatSearchRequest
, this);
500 catch (ProcessCanceledException e
) {
503 catch (Exception e
) {
507 if (RUN_GLOBAL_TOOLS_ONLY
) return;
509 final PsiManager psiManager
= PsiManager
.getInstance(myProject
);
510 scope
.accept(new PsiRecursiveElementVisitor() {
512 public void visitFile(PsiFile file
) {
514 final VirtualFile virtualFile
= file
.getVirtualFile();
515 if (virtualFile
!= null) {
516 incrementJobDoneAmount(LOCAL_ANALYSIS
, ProjectUtil
.calcRelativeToProjectPath(virtualFile
, myProject
));
519 final FileViewProvider viewProvider
= psiManager
.findViewProvider(virtualFile
);
520 final com
.intellij
.openapi
.editor
.Document document
= viewProvider
!= null ? viewProvider
.getDocument() : null;
521 if (document
== null || virtualFile
.getFileType().isBinary()) return; //do not inspect binary files
522 final LocalInspectionsPass pass
= new LocalInspectionsPass(file
, document
, 0, file
.getTextLength());
524 final List
<InspectionProfileEntry
> lTools
= new ArrayList
<InspectionProfileEntry
>();
525 for (Tools tool
: localTools
) {
526 final InspectionTool enabledTool
= (InspectionTool
)tool
.getEnabledTool(file
);
527 if (enabledTool
!= null) {
528 lTools
.add(enabledTool
);
531 pass
.doInspectInBatch((InspectionManagerEx
)manager
, lTools
.toArray(new InspectionProfileEntry
[lTools
.size()]), true);
533 catch (ProcessCanceledException e
) {
536 catch (Exception e
) {
543 public void initializeTools(List
<Tools
> tools
, List
<Tools
> localTools
) {
544 myJobDescriptors
= new ArrayList
<JobDescriptor
>();
545 final InspectionProfileImpl profile
= new InspectionProfileImpl((InspectionProfileImpl
)getCurrentProfile());
546 final List
<ToolsImpl
> usedTools
= profile
.getAllEnabledInspectionTools();
547 for (Tools currentTools
: usedTools
) {
548 final String shortName
= currentTools
.getShortName();
549 myTools
.put(shortName
, currentTools
);
550 final InspectionTool tool
= (InspectionTool
)currentTools
.getTool();
551 if (tool
instanceof LocalInspectionToolWrapper
) {
552 localTools
.add(currentTools
);
553 appendJobDescriptor(LOCAL_ANALYSIS
);
556 tools
.add(currentTools
);
557 JobDescriptor
[] jobDescriptors
= tool
.getJobDescriptors();
558 for (JobDescriptor jobDescriptor
: jobDescriptors
) {
559 appendJobDescriptor(jobDescriptor
);
563 for (ScopeToolState state
: currentTools
.getTools()) {
564 ((InspectionTool
)state
.getTool()).initialize(this);
567 for (GlobalInspectionContextExtension extension
: myExtensions
.values()) {
568 extension
.performPreRunActivities(tools
, localTools
, this);
572 public Map
<String
, Tools
> getTools() {
576 private void appendJobDescriptor(JobDescriptor job
) {
577 if (!myJobDescriptors
.contains(job
)) {
578 myJobDescriptors
.add(job
);
579 job
.setDoneAmount(0);
583 public void close(boolean noSuspisiousCodeFound
) {
584 if (!noSuspisiousCodeFound
&& (myView
== null || myView
.isRerun())) return;
585 final InspectionManagerEx managerEx
= (InspectionManagerEx
)InspectionManager
.getInstance(myProject
);
587 AnalysisUIOptions
.getInstance(myProject
).save(myUIOptions
);
588 if (myContent
!= null) {
589 final ContentManager contentManager
= getContentManager();
590 if (contentManager
!= null) { //null for tests
591 contentManager
.removeContent(myContent
, true);
597 public void cleanup(final InspectionManagerEx managerEx
) {
598 managerEx
.closeRunningContext(this);
599 for (Tools tools
: myTools
.values()) {
600 for (ScopeToolState state
: tools
.getTools()) {
601 ((InspectionTool
)state
.getTool()).finalCleanup();
607 public void refreshViews() {
608 if (myView
!= null) {
609 myView
.updateView(false);
613 public void incrementJobDoneAmount(JobDescriptor job
, String message
) {
614 if (myProgressIndicator
== null) return;
616 ProgressManager
.checkCanceled();
618 int old
= job
.getDoneAmount();
619 job
.setDoneAmount(old
+ 1);
621 int jobCount
= myJobDescriptors
.size();
622 float totalProgress
= 0;
623 for (JobDescriptor jobDescriptor
: myJobDescriptors
) {
624 totalProgress
+= jobDescriptor
.getProgress();
627 totalProgress
/= jobCount
;
629 myProgressIndicator
.setFraction(totalProgress
);
630 myProgressIndicator
.setText(job
.getDisplayName() + " " + message
);
633 public void setExternalProfile(InspectionProfile profile
) {
634 myExternalProfile
= profile
;