update copyrights
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInspection / ex / QuickFixAction.java
blob769e7f3ece85bdd87e8e93b0bb133d58283816c7
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.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;
46 import javax.swing.*;
47 import java.util.*;
49 /**
50 * @author max
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);
65 myTool = tool;
66 if (keyStroke != null) {
67 registerCustomShortcutSet(new CustomShortcutSet(keyStroke), null);
71 public void update(AnActionEvent e) {
72 final InspectionResultsView view = getInvoker(e);
73 if (view == null) {
74 e.getPresentation().setEnabled(false);
75 return;
78 final InspectionTree tree = view.getTree();
79 if (!view.isSingleToolInSelection() || tree.getSelectedTool() != myTool) {
80 e.getPresentation().setVisible(false);
81 e.getPresentation().setEnabled(false);
82 return;
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() {
93 return false;
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);
108 return;
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();
137 try {
138 final Set<PsiElement> ignoredElements = new HashSet<PsiElement>();
140 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
141 public void run() {
142 CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
143 ApplicationManager.getApplication().runWriteAction(new Runnable() {
144 public void run() {
145 final PsiModificationTracker tracker = PsiManager.getInstance(project).getModificationTracker();
146 for (CommonProblemDescriptor descriptor : descriptors) {
147 if (descriptor == null) continue;
148 final QuickFix[] fixes = descriptor.getFixes();
149 if (fixes != null) {
150 for (QuickFix fix : fixes) {
151 if (fix != null) {
152 final QuickFixAction quickFixAction = QuickFixAction.this;
153 if (quickFixAction instanceof LocalQuickFixWrapper &&
154 !((LocalQuickFixWrapper)quickFixAction).getFix().getClass().isInstance(fix)) {
155 continue;
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();
191 try {
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() {
196 public void run() {
197 CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
198 ApplicationManager.getApplication().runWriteAction(new Runnable() {
199 public void run() {
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();
250 if (i1 < i2) {
251 return 1;
252 } else if (i1 > i2){
253 return -1;
255 return 0;
257 return containingFile1.getName().compareTo(containingFile2.getName());
259 if (o1 instanceof RefElement) {
260 return 1;
262 if (o2 instanceof RefElement) {
263 return -1;
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;
304 return true;