ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInspection / ex / GlobalInspectionContextImpl.java
blob3e3dd8df1e654a23a2fc414c65c6c412b65980fb
1 /*
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;
62 import javax.swing.*;
63 import java.io.*;
64 import java.util.ArrayList;
65 import java.util.List;
66 import java.util.Map;
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) {
100 myProject = project;
102 myUIOptions = AnalysisUIOptions.getInstance(myProject).copy();
103 myRefManager = null;
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);
112 @NotNull
113 public Project getProject() {
114 return myProject;
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) {
136 //can't be
137 return null;
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;
164 return false;
166 return true;
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) {
184 close(false);
186 myContent = null;
191 myView = view;
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()) {
213 extension.cleanup();
216 for (Tools tools : myTools.values()) {
217 for (ScopeToolState state : tools.getTools()) {
218 ((InspectionTool)state.getTool()).cleanup();
221 myTools.clear();
223 //EntryPointsManager.getInstance(getProject()).cleanup();
225 if (myRefManager != null) {
226 ((RefManagerImpl)myRefManager).cleanup();
227 myRefManager = null;
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;
242 cleanup();
243 if (myContent != null) {
244 getContentManager().removeContent(myContent, true);
247 ApplicationManager.getApplication().invokeLater(new Runnable() {
248 public void run() {
249 myCurrentScope = scope;
250 launchInspections(scope, manager);
256 @NotNull
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);
265 return myRefManager;
268 public void launchInspectionsOffline(final AnalysisScope scope,
269 final String outputPath,
270 final boolean runWithEditorSettings,
271 final boolean runGlobalToolsOnly,
272 final InspectionManager manager) {
273 cleanup();
275 myCurrentScope = scope;
277 InspectionTool.setOutputPath(outputPath);
278 final boolean oldToolsSettings = RUN_GLOBAL_TOOLS_ONLY;
279 RUN_GLOBAL_TOOLS_ONLY = runGlobalToolsOnly;
280 try {
281 ApplicationManager.getApplication().runReadAction(new Runnable() {
282 public void run() {
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();
297 isLocalTool = true;
299 else {
300 tool.updateContent();
301 if (tool.hasReportedProblems()) {
302 hasProblems = true;
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;
312 try {
313 new File(outputPath).mkdirs();
314 final File file = new File(outputPath, toolName + ext);
315 if (isLocalTool) {
316 outStream = new BufferedOutputStream(new FileOutputStream(file, true));
317 outStream.write(("</" + InspectionsBundle.message("inspection.problems") + ">").getBytes());
319 else {
320 PathMacroManager.getInstance(getProject()).collapsePaths(doc.getRootElement());
321 outStream = new BufferedOutputStream(new FileOutputStream(file));
322 JDOMUtil.writeDocument(doc, outStream, "\n");
325 catch (IOException e) {
326 LOG.error(e);
328 finally {
329 if (outStream != null) {
330 try {
331 outStream.close();
333 catch (IOException e) {
334 LOG.error(e);
343 finally {
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) {
356 if (true) {
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;
364 return false;
366 return true;
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());
372 if (tools != null){
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() {
392 return myUIOptions;
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() {
405 public void run() {
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);
417 @Override
418 public void onSuccess() {
419 SwingUtilities.invokeLater(new Runnable() {
420 public void run() {
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());
429 close(true);
431 else {
432 addView(view);
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();
445 try {
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/%
453 public void run() {
454 runTools(needRepeatSearchRequest, scope, manager);
456 }, ProgressWrapper.wrap(myProgressIndicator));
458 catch (ProcessCanceledException e) {
459 cleanup((InspectionManagerEx)manager);
460 throw e;
462 catch (Exception e) {
463 LOG.error(e);
465 finally {
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();
479 try {
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) {
489 throw e;
491 catch (Exception e) {
492 LOG.error(e);
496 for (GlobalInspectionContextExtension extension : myExtensions.values()) {
497 try {
498 extension.performPostRunActivities(needRepeatSearchRequest, this);
500 catch (ProcessCanceledException e) {
501 throw e;
503 catch (Exception e) {
504 LOG.error(e);
507 if (RUN_GLOBAL_TOOLS_ONLY) return;
509 final PsiManager psiManager = PsiManager.getInstance(myProject);
510 scope.accept(new PsiRecursiveElementVisitor() {
511 @Override
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());
523 try {
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) {
534 throw e;
536 catch (Exception e) {
537 LOG.error(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);
555 else {
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() {
573 return myTools;
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);
586 cleanup(managerEx);
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);
594 myView = null;
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();
604 cleanup();
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;