| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.intellij.codeInspection.ex; |
| |
| import com.intellij.codeInsight.FileModificationService; |
| import com.intellij.codeInspection.CommonProblemDescriptor; |
| import com.intellij.codeInspection.InspectionManager; |
| import com.intellij.codeInspection.ProblemDescriptor; |
| import com.intellij.codeInspection.reference.RefElement; |
| import com.intellij.codeInspection.reference.RefEntity; |
| import com.intellij.codeInspection.reference.RefManagerImpl; |
| import com.intellij.codeInspection.ui.InspectionResultsView; |
| import com.intellij.codeInspection.ui.InspectionTree; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.AnAction; |
| import com.intellij.openapi.actionSystem.AnActionEvent; |
| import com.intellij.openapi.actionSystem.CustomShortcutSet; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.vfs.ReadonlyStatusHandler; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.presentation.java.SymbolPresentationUtil; |
| import com.intellij.util.SequentialModalProgressTask; |
| import com.intellij.util.SequentialTask; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.util.*; |
| |
| /** |
| * @author max |
| */ |
| public class QuickFixAction extends AnAction { |
| protected final InspectionToolWrapper myToolWrapper; |
| |
| public static InspectionResultsView getInvoker(AnActionEvent e) { |
| return InspectionResultsView.DATA_KEY.getData(e.getDataContext()); |
| } |
| |
| protected QuickFixAction(String text, @NotNull InspectionToolWrapper toolWrapper) { |
| this(text, AllIcons.Actions.CreateFromUsage, null, toolWrapper); |
| } |
| |
| protected QuickFixAction(String text, Icon icon, KeyStroke keyStroke, @NotNull InspectionToolWrapper toolWrapper) { |
| super(text, null, icon); |
| myToolWrapper = toolWrapper; |
| if (keyStroke != null) { |
| registerCustomShortcutSet(new CustomShortcutSet(keyStroke), null); |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| final InspectionResultsView view = getInvoker(e); |
| if (view == null) { |
| e.getPresentation().setEnabled(false); |
| return; |
| } |
| |
| e.getPresentation().setVisible(false); |
| e.getPresentation().setEnabled(false); |
| |
| final InspectionTree tree = view.getTree(); |
| final InspectionToolWrapper toolWrapper = tree.getSelectedToolWrapper(); |
| if (!view.isSingleToolInSelection() || toolWrapper != myToolWrapper) { |
| return; |
| } |
| |
| if (!isProblemDescriptorsAcceptable() && tree.getSelectedElements().length > 0 || |
| isProblemDescriptorsAcceptable() && tree.getSelectedDescriptors().length > 0) { |
| e.getPresentation().setVisible(true); |
| e.getPresentation().setEnabled(true); |
| } |
| } |
| |
| protected boolean isProblemDescriptorsAcceptable() { |
| return false; |
| } |
| |
| public String getText(RefEntity where) { |
| return getTemplatePresentation().getText(); |
| } |
| |
| @Override |
| public void actionPerformed(final AnActionEvent e) { |
| final InspectionResultsView view = getInvoker(e); |
| final InspectionTree tree = view.getTree(); |
| if (isProblemDescriptorsAcceptable()) { |
| final CommonProblemDescriptor[] descriptors = tree.getSelectedDescriptors(); |
| if (descriptors.length > 0) { |
| doApplyFix(view.getProject(), descriptors, tree.getContext()); |
| return; |
| } |
| } |
| |
| doApplyFix(getSelectedElements(e), view); |
| } |
| |
| |
| protected void applyFix(@NotNull Project project, |
| @NotNull GlobalInspectionContextImpl context, |
| @NotNull CommonProblemDescriptor[] descriptors, |
| @NotNull Set<PsiElement> ignoredElements) { |
| } |
| |
| private void doApplyFix(@NotNull final Project project, |
| @NotNull final CommonProblemDescriptor[] descriptors, |
| @NotNull final GlobalInspectionContextImpl context) { |
| final Set<VirtualFile> readOnlyFiles = new THashSet<VirtualFile>(); |
| for (CommonProblemDescriptor descriptor : descriptors) { |
| final PsiElement psiElement = descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null; |
| if (psiElement != null && !psiElement.isWritable()) { |
| readOnlyFiles.add(psiElement.getContainingFile().getVirtualFile()); |
| } |
| } |
| |
| if (!FileModificationService.getInstance().prepareVirtualFilesForWrite(project, readOnlyFiles)) return; |
| |
| final RefManagerImpl refManager = (RefManagerImpl)context.getRefManager(); |
| |
| final boolean initial = refManager.isInProcess(); |
| |
| refManager.inspectionReadActionFinished(); |
| |
| try { |
| final Set<PsiElement> ignoredElements = new HashSet<PsiElement>(); |
| |
| CommandProcessor.getInstance().executeCommand(project, new Runnable() { |
| @Override |
| public void run() { |
| CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| final SequentialModalProgressTask progressTask = |
| new SequentialModalProgressTask(project, getTemplatePresentation().getText(), false); |
| progressTask.setMinIterationTime(200); |
| progressTask.setTask(new PerformFixesTask(project, descriptors, ignoredElements, progressTask, context)); |
| ProgressManager.getInstance().run(progressTask); |
| } |
| }); |
| } |
| }, getTemplatePresentation().getText(), null); |
| |
| refreshViews(project, ignoredElements, myToolWrapper); |
| } |
| finally { //to make offline view lazy |
| if (initial) refManager.inspectionReadActionStarted(); |
| } |
| } |
| |
| public void doApplyFix(@NotNull final RefEntity[] refElements, @NotNull InspectionResultsView view) { |
| final RefManagerImpl refManager = (RefManagerImpl)view.getGlobalInspectionContext().getRefManager(); |
| |
| final boolean initial = refManager.isInProcess(); |
| |
| refManager.inspectionReadActionFinished(); |
| |
| try { |
| final boolean[] refreshNeeded = {false}; |
| if (refElements.length > 0) { |
| final Project project = refElements[0].getRefManager().getProject(); |
| CommandProcessor.getInstance().executeCommand(project, new Runnable() { |
| @Override |
| public void run() { |
| CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| refreshNeeded[0] = applyFix(refElements); |
| } |
| }); |
| } |
| }, getTemplatePresentation().getText(), null); |
| } |
| if (refreshNeeded[0]) { |
| refreshViews(view.getProject(), refElements, myToolWrapper); |
| } |
| } |
| finally { //to make offline view lazy |
| if (initial) refManager.inspectionReadActionStarted(); |
| } |
| } |
| |
| public static void removeElements(@NotNull RefEntity[] refElements, @NotNull Project project, @NotNull InspectionToolWrapper toolWrapper) { |
| refreshViews(project, refElements, toolWrapper); |
| final ArrayList<RefElement> deletedRefs = new ArrayList<RefElement>(1); |
| for (RefEntity refElement : refElements) { |
| if (!(refElement instanceof RefElement)) continue; |
| refElement.getRefManager().removeRefElement((RefElement)refElement, deletedRefs); |
| } |
| } |
| |
| private static Set<VirtualFile> getReadOnlyFiles(@NotNull RefEntity[] refElements) { |
| Set<VirtualFile> readOnlyFiles = new THashSet<VirtualFile>(); |
| for (RefEntity refElement : refElements) { |
| PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getElement() : null; |
| if (psiElement == null || psiElement.getContainingFile() == null) continue; |
| readOnlyFiles.add(psiElement.getContainingFile().getVirtualFile()); |
| } |
| return readOnlyFiles; |
| } |
| |
| private static RefEntity[] getSelectedElements(AnActionEvent e) { |
| final InspectionResultsView invoker = getInvoker(e); |
| if (invoker == null) return new RefElement[0]; |
| List<RefEntity> selection = new ArrayList<RefEntity>(Arrays.asList(invoker.getTree().getSelectedElements())); |
| PsiDocumentManager.getInstance(invoker.getProject()).commitAllDocuments(); |
| Collections.sort(selection, new Comparator<RefEntity>() { |
| @Override |
| public int compare(RefEntity o1, RefEntity o2) { |
| if (o1 instanceof RefElement && o2 instanceof RefElement) { |
| RefElement r1 = (RefElement)o1; |
| RefElement r2 = (RefElement)o2; |
| final PsiElement element1 = r1.getElement(); |
| final PsiElement element2 = r2.getElement(); |
| final PsiFile containingFile1 = element1.getContainingFile(); |
| final PsiFile containingFile2 = element2.getContainingFile(); |
| if (containingFile1 == containingFile2) { |
| int i1 = element1.getTextOffset(); |
| int i2 = element2.getTextOffset(); |
| if (i1 < i2) { |
| return 1; |
| } else if (i1 > i2){ |
| return -1; |
| } |
| return 0; |
| } |
| return containingFile1.getName().compareTo(containingFile2.getName()); |
| } |
| if (o1 instanceof RefElement) { |
| return 1; |
| } |
| if (o2 instanceof RefElement) { |
| return -1; |
| } |
| return o1.getName().compareTo(o2.getName()); |
| } |
| }); |
| |
| return selection.toArray(new RefEntity[selection.size()]); |
| } |
| |
| private static void refreshViews(@NotNull Project project, @NotNull Set<PsiElement> selectedElements, @NotNull InspectionToolWrapper toolWrapper) { |
| InspectionManagerEx managerEx = (InspectionManagerEx)InspectionManager.getInstance(project); |
| final Set<GlobalInspectionContextImpl> runningContexts = managerEx.getRunningContexts(); |
| for (GlobalInspectionContextImpl context : runningContexts) { |
| for (PsiElement element : selectedElements) { |
| context.ignoreElement(toolWrapper.getTool(), element); |
| } |
| context.refreshViews(); |
| } |
| } |
| |
| private static void refreshViews(@NotNull Project project, @NotNull RefEntity[] refElements, @NotNull InspectionToolWrapper toolWrapper) { |
| final Set<PsiElement> ignoredElements = new HashSet<PsiElement>(); |
| for (RefEntity element : refElements) { |
| final PsiElement psiElement = element instanceof RefElement ? ((RefElement)element).getElement() : null; |
| if (psiElement != null && psiElement.isValid()) { |
| ignoredElements.add(psiElement); |
| } |
| } |
| refreshViews(project, ignoredElements, toolWrapper); |
| } |
| |
| /** |
| * @return true if immediate UI update needed. |
| */ |
| protected boolean applyFix(@NotNull RefEntity[] refElements) { |
| Set<VirtualFile> readOnlyFiles = getReadOnlyFiles(refElements); |
| if (!readOnlyFiles.isEmpty()) { |
| final Project project = refElements[0].getRefManager().getProject(); |
| final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler.getInstance(project).ensureFilesWritable( |
| VfsUtilCore.toVirtualFileArray(readOnlyFiles)); |
| if (operationStatus.hasReadonlyFiles()) return false; |
| } |
| return true; |
| } |
| |
| private class PerformFixesTask implements SequentialTask { |
| @NotNull |
| private final Project myProject; |
| private final CommonProblemDescriptor[] myDescriptors; |
| @NotNull |
| private final Set<PsiElement> myIgnoredElements; |
| private final SequentialModalProgressTask myTask; |
| @NotNull private final GlobalInspectionContextImpl myContext; |
| private int myCount = 0; |
| |
| public PerformFixesTask(@NotNull Project project, |
| @NotNull CommonProblemDescriptor[] descriptors, |
| @NotNull Set<PsiElement> ignoredElements, |
| @NotNull SequentialModalProgressTask task, |
| @NotNull GlobalInspectionContextImpl context) { |
| myProject = project; |
| myDescriptors = descriptors; |
| myIgnoredElements = ignoredElements; |
| myTask = task; |
| myContext = context; |
| } |
| |
| @Override |
| public void prepare() { |
| } |
| |
| @Override |
| public boolean isDone() { |
| return myCount > myDescriptors.length - 1; |
| } |
| |
| @Override |
| public boolean iteration() { |
| final CommonProblemDescriptor descriptor = myDescriptors[myCount++]; |
| ProgressIndicator indicator = myTask.getIndicator(); |
| if (indicator != null) { |
| indicator.setFraction((double)myCount / myDescriptors.length); |
| if (descriptor instanceof ProblemDescriptor) { |
| final PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement(); |
| if (psiElement != null) { |
| indicator.setText("Processing " + SymbolPresentationUtil.getSymbolPresentableText(psiElement)); |
| } |
| } |
| } |
| applyFix(myProject, myContext, new CommonProblemDescriptor[]{descriptor}, myIgnoredElements); |
| return isDone(); |
| } |
| |
| @Override |
| public void stop() { |
| } |
| } |
| } |