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
.ide
.util
;
19 import com
.intellij
.CommonBundle
;
20 import com
.intellij
.history
.LocalHistory
;
21 import com
.intellij
.history
.LocalHistoryAction
;
22 import com
.intellij
.ide
.DataManager
;
23 import com
.intellij
.ide
.DeleteProvider
;
24 import com
.intellij
.ide
.IdeBundle
;
25 import com
.intellij
.openapi
.actionSystem
.DataConstants
;
26 import com
.intellij
.openapi
.actionSystem
.DataContext
;
27 import com
.intellij
.openapi
.actionSystem
.PlatformDataKeys
;
28 import com
.intellij
.openapi
.application
.ApplicationManager
;
29 import com
.intellij
.openapi
.command
.CommandProcessor
;
30 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
31 import com
.intellij
.openapi
.project
.DumbService
;
32 import com
.intellij
.openapi
.project
.Project
;
33 import com
.intellij
.openapi
.ui
.Messages
;
34 import com
.intellij
.openapi
.ui
.ex
.MessagesEx
;
35 import com
.intellij
.openapi
.vfs
.VirtualFile
;
36 import com
.intellij
.psi
.PsiCompiledElement
;
37 import com
.intellij
.psi
.PsiDirectory
;
38 import com
.intellij
.psi
.PsiElement
;
39 import com
.intellij
.psi
.PsiFile
;
40 import com
.intellij
.psi
.util
.PsiTreeUtil
;
41 import com
.intellij
.refactoring
.RefactoringBundle
;
42 import com
.intellij
.refactoring
.safeDelete
.SafeDeleteProcessor
;
43 import com
.intellij
.refactoring
.util
.CommonRefactoringUtil
;
44 import com
.intellij
.refactoring
.util
.RefactoringUIUtil
;
45 import com
.intellij
.util
.IncorrectOperationException
;
46 import com
.intellij
.util
.io
.ReadOnlyAttributeUtil
;
47 import org
.jetbrains
.annotations
.Nullable
;
49 import java
.io
.IOException
;
50 import java
.util
.ArrayList
;
51 import java
.util
.Arrays
;
53 public class DeleteHandler
{
54 public static class DefaultDeleteProvider
implements DeleteProvider
{
55 public boolean canDeleteElement(DataContext dataContext
) {
56 if (dataContext
.getData(DataConstants
.PROJECT
) == null) return false;
57 final PsiElement
[] elements
= getPsiElements(dataContext
);
58 return elements
!= null && DeleteHandler
.shouldEnableDeleteAction(elements
);
62 private PsiElement
[] getPsiElements(DataContext dataContext
) {
63 PsiElement
[] elements
= (PsiElement
[])dataContext
.getData(DataConstants
.PSI_ELEMENT_ARRAY
);
64 if (elements
== null) {
65 final Object data
= dataContext
.getData(DataConstants
.PSI_ELEMENT
);
67 elements
= new PsiElement
[]{(PsiElement
)data
};
70 final Object data1
= dataContext
.getData(DataConstants
.PSI_FILE
);
72 elements
= new PsiElement
[]{(PsiFile
)data1
};
79 public void deleteElement(DataContext dataContext
) {
80 PsiElement
[] elements
= getPsiElements(dataContext
);
81 if (elements
== null) return;
82 Project project
= PlatformDataKeys
.PROJECT
.getData(dataContext
);
83 if (project
== null) return;
84 LocalHistoryAction a
= LocalHistory
.startAction(project
, IdeBundle
.message("progress.deleting"));
86 deletePsiElement(elements
, project
);
94 public static void deletePsiElement(final PsiElement
[] elementsToDelete
, final Project project
) {
95 if (elementsToDelete
== null || elementsToDelete
.length
== 0) return;
97 final PsiElement
[] elements
= PsiTreeUtil
.filterAncestors(elementsToDelete
);
99 boolean safeDeleteApplicable
= true;
100 for (int i
= 0; i
< elements
.length
&& safeDeleteApplicable
; i
++) {
101 PsiElement element
= elements
[i
];
102 safeDeleteApplicable
= SafeDeleteProcessor
.validElement(element
);
105 final boolean dumb
= DumbService
.getInstance(project
).isDumb();
106 if (safeDeleteApplicable
&& !dumb
) {
107 DeleteDialog dialog
= new DeleteDialog(project
, elements
, new DeleteDialog
.Callback() {
108 public void run(final DeleteDialog dialog
) {
109 if (!CommonRefactoringUtil
.checkReadOnlyStatusRecursively(project
, Arrays
.asList(elements
))) return;
110 SafeDeleteProcessor
.createInstance(project
, new Runnable() {
112 dialog
.close(DeleteDialog
.CANCEL_EXIT_CODE
);
114 }, elements
, dialog
.isSearchInComments(), dialog
.isSearchInNonJava(), true).run();
118 if (!dialog
.isOK()) return;
121 @SuppressWarnings({"UnresolvedPropertyKey"})
122 String warningMessage
= DeleteUtil
.generateWarningMessage(IdeBundle
.message("prompt.delete.elements"), elements
);
124 boolean anyDirectories
= false;
125 String directoryName
= null;
126 for (PsiElement psiElement
: elementsToDelete
) {
127 if (psiElement
instanceof PsiDirectory
) {
128 anyDirectories
= true;
129 directoryName
= ((PsiDirectory
)psiElement
).getName();
133 if (anyDirectories
) {
134 if (elements
.length
== 1) {
135 warningMessage
+= IdeBundle
.message("warning.delete.all.files.and.subdirectories", directoryName
);
138 warningMessage
+= IdeBundle
.message("warning.delete.all.files.and.subdirectories.in.the.selected.directory");
142 if (safeDeleteApplicable
&& dumb
) {
143 warningMessage
+= "\n\nWarning:\n Safe delete is not available while IntelliJ IDEA updates indices,\n no usages will be checked.";
146 int result
= Messages
.showDialog(project
, warningMessage
, IdeBundle
.message("title.delete"),
147 new String
[]{CommonBundle
.getOkButtonText(), CommonBundle
.getCancelButtonText()}, 0,
148 Messages
.getQuestionIcon());
149 if (result
!= 0) return;
152 final FileTypeManager ftManager
= FileTypeManager
.getInstance();
153 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
155 CommonRefactoringUtil
.checkReadOnlyStatusRecursively(project
, Arrays
.asList(elements
), false);
157 // deleted from project view or something like that.
158 if (PlatformDataKeys
.EDITOR
.getData(DataManager
.getInstance().getDataContext()) == null) {
159 CommandProcessor
.getInstance().markCurrentCommandAsGlobal(project
);
162 for (final PsiElement elementToDelete
: elements
) {
163 if (!elementToDelete
.isValid()) continue; //was already deleted
164 if (elementToDelete
instanceof PsiDirectory
) {
165 VirtualFile virtualFile
= ((PsiDirectory
)elementToDelete
).getVirtualFile();
166 if (virtualFile
.isInLocalFileSystem()) {
168 ArrayList
<VirtualFile
> readOnlyFiles
= new ArrayList
<VirtualFile
>();
169 getReadOnlyVirtualFiles(virtualFile
, readOnlyFiles
, ftManager
);
171 if (readOnlyFiles
.size() > 0) {
172 int _result
= Messages
.showYesNoDialog(project
, IdeBundle
.message("prompt.directory.contains.read.only.files",
173 virtualFile
.getPresentableUrl()),
174 IdeBundle
.message("title.delete"), Messages
.getQuestionIcon());
175 if (_result
!= 0) continue;
177 boolean success
= true;
178 for (VirtualFile file
: readOnlyFiles
) {
179 success
= clearReadOnlyFlag(file
, project
);
182 if (!success
) continue;
186 else if (!elementToDelete
.isWritable()) {
187 final PsiFile file
= elementToDelete
.getContainingFile();
189 final VirtualFile virtualFile
= file
.getVirtualFile();
190 if (virtualFile
.isInLocalFileSystem()) {
191 int _result
= MessagesEx
.fileIsReadOnly(project
, virtualFile
)
192 .setTitle(IdeBundle
.message("title.delete"))
193 .appendMessage(IdeBundle
.message("prompt.delete.it.anyway"))
195 if (_result
!= 0) continue;
197 boolean success
= clearReadOnlyFlag(virtualFile
, project
);
198 if (!success
) continue;
204 elementToDelete
.checkDelete();
206 catch (IncorrectOperationException ex
) {
207 Messages
.showMessageDialog(project
, ex
.getMessage(), CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
211 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
214 elementToDelete
.delete();
216 catch (final IncorrectOperationException ex
) {
217 ApplicationManager
.getApplication().invokeLater(new Runnable() {
219 Messages
.showMessageDialog(project
, ex
.getMessage(), CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
227 }, RefactoringBundle
.message("safe.delete.command", RefactoringUIUtil
.calculatePsiElementDescriptionList(elements
)), null);
230 private static boolean clearReadOnlyFlag(final VirtualFile virtualFile
, final Project project
) {
231 final boolean[] success
= new boolean[1];
232 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
234 Runnable action
= new Runnable() {
237 ReadOnlyAttributeUtil
.setReadOnlyAttribute(virtualFile
, false);
240 catch (IOException e1
) {
241 Messages
.showMessageDialog(project
, e1
.getMessage(), CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
245 ApplicationManager
.getApplication().runWriteAction(action
);
252 * Fills readOnlyFiles with VirtualFiles
254 private static void getReadOnlyVirtualFiles(VirtualFile file
, ArrayList
<VirtualFile
> readOnlyFiles
, final FileTypeManager ftManager
) {
255 if (ftManager
.isFileIgnored(file
.getName())) return;
256 if (!file
.isWritable()) {
257 readOnlyFiles
.add(file
);
259 if (file
.isDirectory()) {
260 VirtualFile
[] children
= file
.getChildren();
261 for (VirtualFile child
: children
) {
262 getReadOnlyVirtualFiles(child
, readOnlyFiles
, ftManager
);
267 public static boolean shouldEnableDeleteAction(PsiElement
[] elements
) {
268 if (elements
== null || elements
.length
== 0) return false;
269 for (PsiElement element
: elements
) {
270 if (element
instanceof PsiCompiledElement
) {