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
.codeInsight
.daemon
.DaemonCodeAnalyzer
;
20 import com
.intellij
.codeInspection
.CommonProblemDescriptor
;
21 import com
.intellij
.codeInspection
.InspectionManager
;
22 import com
.intellij
.codeInspection
.ProblemDescriptor
;
23 import com
.intellij
.codeInspection
.QuickFix
;
24 import com
.intellij
.codeInspection
.reference
.RefElement
;
25 import com
.intellij
.codeInspection
.reference
.RefEntity
;
26 import com
.intellij
.codeInspection
.reference
.RefManagerImpl
;
27 import com
.intellij
.codeInspection
.ui
.InspectionResultsView
;
28 import com
.intellij
.codeInspection
.ui
.InspectionTree
;
29 import com
.intellij
.openapi
.actionSystem
.AnAction
;
30 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
31 import com
.intellij
.openapi
.actionSystem
.CustomShortcutSet
;
32 import com
.intellij
.openapi
.actionSystem
.ex
.DataConstantsEx
;
33 import com
.intellij
.openapi
.application
.ApplicationManager
;
34 import com
.intellij
.openapi
.command
.CommandProcessor
;
35 import com
.intellij
.openapi
.project
.Project
;
36 import com
.intellij
.openapi
.util
.IconLoader
;
37 import com
.intellij
.openapi
.vfs
.ReadonlyStatusHandler
;
38 import com
.intellij
.openapi
.vfs
.VirtualFile
;
39 import com
.intellij
.psi
.PsiDocumentManager
;
40 import com
.intellij
.psi
.PsiElement
;
41 import com
.intellij
.psi
.PsiFile
;
42 import com
.intellij
.psi
.PsiManager
;
43 import com
.intellij
.psi
.util
.PsiModificationTracker
;
44 import gnu
.trove
.THashSet
;
52 public class QuickFixAction
extends AnAction
{
53 protected InspectionTool myTool
;
55 public static InspectionResultsView
getInvoker(AnActionEvent e
) {
56 return (InspectionResultsView
)e
.getDataContext().getData(DataConstantsEx
.INSPECTION_VIEW
);
59 protected QuickFixAction(String text
, InspectionTool tool
) {
60 this(text
, IconLoader
.getIcon("/actions/createFromUsage.png"), null, tool
);
63 protected QuickFixAction(String text
, Icon icon
, KeyStroke keyStroke
, InspectionTool tool
) {
64 super(text
, null, icon
);
66 if (keyStroke
!= null) {
67 registerCustomShortcutSet(new CustomShortcutSet(keyStroke
), null);
71 public void update(AnActionEvent e
) {
72 final InspectionResultsView view
= getInvoker(e
);
74 e
.getPresentation().setEnabled(false);
78 final InspectionTree tree
= view
.getTree();
79 if (!view
.isSingleToolInSelection() || tree
.getSelectedTool() != myTool
) {
80 e
.getPresentation().setVisible(false);
81 e
.getPresentation().setEnabled(false);
85 if (!isProblemDescriptorsAcceptable() && tree
.getSelectedElements().length
> 0 ||
86 isProblemDescriptorsAcceptable() && tree
.getSelectedDescriptors().length
> 0) {
87 e
.getPresentation().setVisible(true);
88 e
.getPresentation().setEnabled(true);
92 protected boolean isProblemDescriptorsAcceptable() {
96 public String
getText(RefEntity where
) {
97 return getTemplatePresentation().getText();
100 public void actionPerformed(final AnActionEvent e
) {
101 final InspectionResultsView view
= getInvoker(e
);
102 final InspectionTree tree
= view
.getTree();
103 final InspectionTool tool
= tree
.getSelectedTool();
104 if (isProblemDescriptorsAcceptable()) {
105 final CommonProblemDescriptor
[] descriptors
= tree
.getSelectedDescriptors();
106 if (descriptors
.length
> 0) {
107 doApplyFix(view
.getProject(), descriptors
);
112 doApplyFix(getSelectedElements(e
), view
);
116 private void doApplyFix(final Project project
,
117 final CommonProblemDescriptor
[] descriptors
) {
118 final Set
<VirtualFile
> readOnlyFiles
= new THashSet
<VirtualFile
>();
119 for (CommonProblemDescriptor descriptor
: descriptors
) {
120 final PsiElement psiElement
= descriptor
instanceof ProblemDescriptor ?
((ProblemDescriptor
)descriptor
).getPsiElement() : null;
121 if (psiElement
!= null && !psiElement
.isWritable()) {
122 readOnlyFiles
.add(psiElement
.getContainingFile().getVirtualFile());
126 if (!readOnlyFiles
.isEmpty()) {
127 final ReadonlyStatusHandler
.OperationStatus operationStatus
= ReadonlyStatusHandler
.getInstance(project
).ensureFilesWritable(readOnlyFiles
.toArray(new VirtualFile
[readOnlyFiles
.size()]));
128 if (operationStatus
.hasReadonlyFiles()) return;
131 final RefManagerImpl refManager
= ((RefManagerImpl
)myTool
.getContext().getRefManager());
133 final boolean initial
= refManager
.isInProcess();
135 refManager
.inspectionReadActionFinished();
138 final Set
<PsiElement
> ignoredElements
= new HashSet
<PsiElement
>();
140 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
142 CommandProcessor
.getInstance().markCurrentCommandAsGlobal(project
);
143 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
145 final PsiModificationTracker tracker
= PsiManager
.getInstance(project
).getModificationTracker();
146 for (CommonProblemDescriptor descriptor
: descriptors
) {
147 if (descriptor
== null) continue;
148 final QuickFix
[] fixes
= descriptor
.getFixes();
150 for (QuickFix fix
: fixes
) {
152 final QuickFixAction quickFixAction
= QuickFixAction
.this;
153 if (quickFixAction
instanceof LocalQuickFixWrapper
&&
154 !((LocalQuickFixWrapper
)quickFixAction
).getFix().getClass().isInstance(fix
)) {
158 final long startCount
= tracker
.getModificationCount();
159 //CCE here means QuickFix was incorrectly inherited, is there a way to signal (plugin) it is wrong?
160 fix
.applyFix(project
, descriptor
);
161 if (startCount
!= tracker
.getModificationCount()) {
162 DaemonCodeAnalyzer
.getInstance(project
).restart();
163 ((DescriptorProviderInspection
)myTool
).ignoreProblem(descriptor
, fix
);
164 if (descriptor
instanceof ProblemDescriptor
) {
165 ignoredElements
.add(((ProblemDescriptor
)descriptor
).getPsiElement());
175 }, getTemplatePresentation().getText(), null);
177 refreshViews(project
, ignoredElements
, myTool
);
179 finally { //to make offline view lazy
180 if (initial
) refManager
.inspectionReadActionStarted();
184 public void doApplyFix(final RefElement
[] refElements
, InspectionResultsView view
) {
185 final RefManagerImpl refManager
= ((RefManagerImpl
)myTool
.getContext().getRefManager());
187 final boolean initial
= refManager
.isInProcess();
189 refManager
.inspectionReadActionFinished();
192 final boolean[] refreshNeeded
= new boolean[]{false};
193 if (refElements
.length
> 0) {
194 final Project project
= refElements
[0].getRefManager().getProject();
195 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
197 CommandProcessor
.getInstance().markCurrentCommandAsGlobal(project
);
198 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
200 refreshNeeded
[0] = applyFix(refElements
);
204 }, getTemplatePresentation().getText(), null);
206 if (refreshNeeded
[0]) {
207 refreshViews(view
.getProject(), refElements
, myTool
);
210 finally { //to make offline view lazy
211 if (initial
) refManager
.inspectionReadActionStarted();
215 public static void removeElements(final RefElement
[] refElements
, final Project project
, final InspectionTool tool
) {
216 refreshViews(project
, refElements
, tool
);
217 final ArrayList
<RefElement
> deletedRefs
= new ArrayList
<RefElement
>(1);
218 for (RefElement refElement
: refElements
) {
219 refElement
.getRefManager().removeRefElement(refElement
, deletedRefs
);
223 private static Set
<VirtualFile
> getReadOnlyFiles(final RefElement
[] refElements
) {
224 Set
<VirtualFile
> readOnlyFiles
= new THashSet
<VirtualFile
>();
225 for (RefElement refElement
: refElements
) {
226 PsiElement psiElement
= refElement
.getElement();
227 if (psiElement
== null) continue;
228 if (!psiElement
.isWritable()) readOnlyFiles
.add(psiElement
.getContainingFile().getVirtualFile());
230 return readOnlyFiles
;
233 private static RefElement
[] getSelectedElements(AnActionEvent e
) {
234 final InspectionResultsView invoker
= getInvoker(e
);
235 if (invoker
== null) return new RefElement
[0];
236 List
<RefEntity
> selection
= new ArrayList
<RefEntity
>(Arrays
.asList(invoker
.getTree().getSelectedElements()));
237 PsiDocumentManager
.getInstance(invoker
.getProject()).commitAllDocuments();
238 Collections
.sort(selection
, new Comparator
<RefEntity
>() {
239 public int compare(RefEntity o1
, RefEntity o2
) {
240 if (o1
instanceof RefElement
&& o2
instanceof RefElement
) {
241 RefElement r1
= (RefElement
)o1
;
242 RefElement r2
= (RefElement
)o2
;
243 final PsiElement element1
= r1
.getElement();
244 final PsiElement element2
= r2
.getElement();
245 final PsiFile containingFile1
= element1
.getContainingFile();
246 final PsiFile containingFile2
= element2
.getContainingFile();
247 if (containingFile1
== containingFile2
) {
248 int i1
= element1
.getTextOffset();
249 int i2
= element2
.getTextOffset();
257 return containingFile1
.getName().compareTo(containingFile2
.getName());
259 if (o1
instanceof RefElement
) {
262 if (o2
instanceof RefElement
) {
265 return o1
.getName().compareTo(o2
.getName());
269 return selection
.toArray(new RefElement
[selection
.size()]);
272 private static void refreshViews(final Project project
, final Set
<PsiElement
> selectedElements
, final InspectionTool tool
) {
273 InspectionManagerEx managerEx
= (InspectionManagerEx
)InspectionManager
.getInstance(project
);
274 final Set
<GlobalInspectionContextImpl
> runningContexts
= managerEx
.getRunningContexts();
275 for (GlobalInspectionContextImpl context
: runningContexts
) {
276 for (PsiElement element
: selectedElements
) {
277 context
.ignoreElement(tool
, element
);
279 context
.refreshViews();
283 private static void refreshViews(final Project project
, final RefElement
[] refElements
, final InspectionTool tool
) {
284 final Set
<PsiElement
> ignoredElements
= new HashSet
<PsiElement
>();
285 for (RefElement element
: refElements
) {
286 final PsiElement psiElement
= element
.getElement();
287 if (psiElement
!= null && psiElement
.isValid()) {
288 ignoredElements
.add(psiElement
);
291 refreshViews(project
, ignoredElements
, tool
);
295 * @return true if immediate UI update needed.
297 protected boolean applyFix(RefElement
[] refElements
) {
298 Set
<VirtualFile
> readOnlyFiles
= getReadOnlyFiles(refElements
);
299 if (!readOnlyFiles
.isEmpty()) {
300 final Project project
= refElements
[0].getRefManager().getProject();
301 final ReadonlyStatusHandler
.OperationStatus operationStatus
= ReadonlyStatusHandler
.getInstance(project
).ensureFilesWritable(readOnlyFiles
.toArray(new VirtualFile
[readOnlyFiles
.size()]));
302 if (operationStatus
.hasReadonlyFiles()) return false;