NPE
[fedora-idea.git] / platform / lang-impl / src / com / intellij / find / replaceInProject / ReplaceInProjectManager.java
blobc967562b6c93dd3e3b365cbf9b3f4b1bf2084136
2 /*
3 * Copyright 2000-2009 JetBrains s.r.o.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package com.intellij.find.replaceInProject;
20 import com.intellij.find.FindBundle;
21 import com.intellij.find.FindManager;
22 import com.intellij.find.FindModel;
23 import com.intellij.find.FindResult;
24 import com.intellij.find.findInProject.FindInProjectManager;
25 import com.intellij.find.impl.FindInProjectUtil;
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.components.ServiceManager;
31 import com.intellij.openapi.editor.Document;
32 import com.intellij.openapi.editor.Editor;
33 import com.intellij.openapi.editor.RangeMarker;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.ui.Messages;
36 import com.intellij.openapi.util.Computable;
37 import com.intellij.openapi.util.Factory;
38 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
39 import com.intellij.openapi.vfs.VfsUtil;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.openapi.wm.WindowManager;
42 import com.intellij.psi.PsiDirectory;
43 import com.intellij.psi.PsiElement;
44 import com.intellij.psi.PsiFile;
45 import com.intellij.usageView.UsageInfo;
46 import com.intellij.usages.*;
47 import com.intellij.usages.rules.UsageInFile;
48 import com.intellij.util.AdapterProcessor;
49 import com.intellij.util.Processor;
50 import org.jetbrains.annotations.NotNull;
52 import javax.swing.*;
53 import java.util.Collection;
54 import java.util.HashSet;
55 import java.util.List;
56 import java.util.Set;
58 public class ReplaceInProjectManager {
59 private final Project myProject;
60 private boolean myIsFindInProgress = false;
62 public static ReplaceInProjectManager getInstance(Project project) {
63 return ServiceManager.getService(project, ReplaceInProjectManager.class);
66 public ReplaceInProjectManager(Project project) {
67 myProject = project;
70 static class ReplaceContext {
71 private final UsageView usageView;
72 private final FindModel findModel;
73 private Set<Usage> excludedSet;
75 ReplaceContext(@NotNull UsageView usageView, @NotNull FindModel findModel) {
76 this.usageView = usageView;
77 this.findModel = findModel;
80 @NotNull
81 public FindModel getFindModel() {
82 return findModel;
85 @NotNull
86 public UsageView getUsageView() {
87 return usageView;
90 @NotNull
91 public Set<Usage> getExcludedSet() {
92 if (excludedSet == null) excludedSet = usageView.getExcludedUsages();
93 return excludedSet;
97 public void replaceInProject(DataContext dataContext) {
98 final FindManager findManager = FindManager.getInstance(myProject);
99 final FindModel findModel = (FindModel) findManager.getFindInProjectModel().clone();
100 findModel.setReplaceState(true);
101 FindInProjectUtil.setDirectoryName(findModel, dataContext);
103 Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
104 if (editor != null){
105 String s = editor.getSelectionModel().getSelectedText();
106 if (s != null && !s.contains("\r") && !s.contains("\n")){
107 findModel.setStringToFind(s);
111 findManager.showFindDialog(findModel, new Runnable() {
112 public void run() {
113 final PsiDirectory psiDirectory = FindInProjectUtil.getPsiDirectory(findModel, myProject);
114 if (!findModel.isProjectScope() && psiDirectory == null && findModel.getModuleName()==null && findModel.getCustomScope() == null){
115 return;
118 UsageViewManager manager = UsageViewManager.getInstance(myProject);
120 if (manager == null) return;
121 findManager.getFindInProjectModel().copyFrom(findModel);
122 final FindModel findModelCopy = (FindModel)findModel.clone();
124 final UsageViewPresentation presentation = FindInProjectUtil.setupViewPresentation(true, findModelCopy);
125 final FindUsagesProcessPresentation processPresentation = FindInProjectUtil.setupProcessPresentation(
126 myProject, true, presentation
129 final ReplaceContext[] context = new ReplaceContext[1];
131 manager.searchAndShowUsages(
132 new UsageTarget[] { new FindInProjectUtil.StringUsageTarget(findModelCopy.getStringToFind()) },
133 new Factory<UsageSearcher>() {
134 public UsageSearcher create() {
135 return new UsageSearcher() {
137 public void generate(final Processor<Usage> processor) {
138 try {
139 myIsFindInProgress = true;
141 FindInProjectUtil.findUsages(
142 findModelCopy,
143 psiDirectory,
144 myProject,
145 new AdapterProcessor<UsageInfo, Usage>(processor, UsageInfo2UsageAdapter.CONVERTER));
147 finally {
148 myIsFindInProgress = false;
154 processPresentation,
155 presentation,
156 new UsageViewManager.UsageViewStateListener() {
157 public void usageViewCreated(UsageView usageView) {
158 context[0] = new ReplaceContext(usageView, findModelCopy);
159 addReplaceActions(context[0]);
162 public void findingUsagesFinished(final UsageView usageView) {
163 if (context[0]!=null && findManager.getFindInProjectModel().isPromptOnReplace()){
164 SwingUtilities.invokeLater(new Runnable() {
165 public void run() {
166 replaceWithPrompt(context[0]);
177 private void replaceWithPrompt(final ReplaceContext replaceContext) {
178 final List<Usage> _usages = replaceContext.getUsageView().getSortedUsages();
180 if (FindInProjectUtil.hasReadOnlyUsages(_usages)){
181 WindowManager.getInstance().getStatusBar(myProject).setInfo(
182 FindBundle.message("find.replace.occurrences.found.in.read.only.files.status"));
183 return;
186 final Usage[] usages = _usages.toArray(new Usage[_usages.size()]);
188 //usageView.expandAll();
189 for(int i = 0; i < usages.length; ++i){
190 final Usage usage = usages[i];
191 final UsageInfo usageInfo = ((UsageInfo2UsageAdapter)usage).getUsageInfo();
193 final PsiElement elt = usageInfo.getElement();
194 if (elt == null) continue;
195 final PsiFile psiFile = elt.getContainingFile();
196 if (!psiFile.isWritable()) continue;
198 Runnable selectOnEditorRunnable = new Runnable() {
199 public void run() {
200 final VirtualFile virtualFile = psiFile.getVirtualFile();
202 if (virtualFile != null &&
203 ApplicationManager.getApplication().runReadAction(
204 new Computable<Boolean>() {
205 public Boolean compute() {
206 return virtualFile.isValid() ? Boolean.TRUE : Boolean.FALSE;
209 ).booleanValue()) {
211 if (usage.isValid()) {
212 usage.highlightInEditor();
213 replaceContext.getUsageView().selectUsages(new Usage[]{usage});
219 CommandProcessor.getInstance().executeCommand(myProject, selectOnEditorRunnable,
220 FindBundle.message("find.replace.select.on.editor.command"), null);
221 String title = FindBundle.message("find.replace.found.usage.title", i + 1, usages.length);
222 int result = FindManager.getInstance(myProject).showPromptDialog(replaceContext.getFindModel(), title);
224 if (result == FindManager.PromptResult.CANCEL){
225 return;
227 if (result == FindManager.PromptResult.SKIP){
228 continue;
231 final int currentNumber = i;
232 if (result == FindManager.PromptResult.OK){
233 Runnable runnable = new Runnable() {
234 public void run() {
235 doReplace(replaceContext, usage);
236 replaceContext.getUsageView().removeUsage(usage);
239 CommandProcessor.getInstance().executeCommand(myProject, runnable, FindBundle.message("find.replace.command"), null);
240 if (i + 1 == usages.length){
241 replaceContext.getUsageView().close();
242 return;
246 if (result == FindManager.PromptResult.ALL_IN_THIS_FILE){
247 final int[] nextNumber = new int[1];
249 Runnable runnable = new Runnable() {
250 public void run() {
251 int j = currentNumber;
253 for(; j < usages.length; j++){
254 final Usage usage = usages[j];
255 final UsageInfo usageInfo = ((UsageInfo2UsageAdapter)usage).getUsageInfo();
257 final PsiElement elt = usageInfo.getElement();
258 if (elt == null) continue;
259 PsiFile otherPsiFile = elt.getContainingFile();
260 if (!otherPsiFile.equals(psiFile)){
261 break;
263 doReplace(replaceContext, usage);
264 replaceContext.getUsageView().removeUsage(usage);
266 if (j == usages.length){
267 replaceContext.getUsageView().close();
269 nextNumber[0] = j;
273 CommandProcessor.getInstance().executeCommand(myProject, runnable, FindBundle.message("find.replace.command"), null);
275 //noinspection AssignmentToForLoopParameter
276 i = nextNumber[0] - 1;
279 if (result == FindManager.PromptResult.ALL_FILES) {
280 CommandProcessor.getInstance().executeCommand(
281 myProject, new Runnable() {
282 public void run() {
283 doReplace(replaceContext, _usages);
284 replaceContext.getUsageView().close();
287 FindBundle.message("find.replace.command"),
288 null
290 break;
295 private void addReplaceActions(final ReplaceContext replaceContext) {
296 final Runnable replaceRunnable = new Runnable() {
297 public void run() {
298 doReplace(replaceContext, replaceContext.getUsageView().getUsages());
301 replaceContext.getUsageView().addPerformOperationAction(replaceRunnable, FindBundle.message("find.replace.all.action"),
302 null, FindBundle.message("find.replace.all.action.description"));
304 final Runnable replaceSelectedRunnable = new Runnable() {
305 public void run() {
306 doReplaceSelected(replaceContext);
310 replaceContext.getUsageView().addButtonToLowerPane(
311 replaceSelectedRunnable,
312 FindBundle.message("find.replace.selected.action")
316 private void doReplace(final ReplaceContext replaceContext, Collection<Usage> usages) {
317 for (final Usage usage : usages) {
318 doReplace(replaceContext, usage);
320 reportNumberReplacedOccurences(myProject, usages.size());
323 public static void reportNumberReplacedOccurences(Project project, int occurrences) {
324 if (occurrences != 0) {
325 WindowManager.getInstance().getStatusBar(project).setInfo(FindBundle.message("0.occurrences.replaced", occurrences));
329 private void doReplace(final ReplaceContext replaceContext, final Usage usage) {
330 ApplicationManager.getApplication().runWriteAction(new Runnable() {
331 public void run() {
332 if (replaceContext.getExcludedSet().contains(usage)){
333 return;
336 List<RangeMarker> markers = ((UsageInfo2UsageAdapter)usage).getRangeMarkers();
337 for (RangeMarker marker : markers) {
338 Document document = marker.getDocument();
339 if (!document.isWritable()) return;
341 final int textOffset = marker.getStartOffset();
342 if (textOffset < 0 || textOffset >= document.getTextLength()){
343 return;
345 final int textEndOffset = marker.getEndOffset();
346 if (textEndOffset < 0 || textOffset > document.getTextLength()){
347 return;
349 FindManager findManager = FindManager.getInstance(myProject);
350 final CharSequence foundString = document.getCharsSequence().subSequence(textOffset, textEndOffset);
351 FindResult findResult = findManager.findString(document.getCharsSequence(), textOffset, replaceContext.getFindModel());
352 if (!findResult.isStringFound()){
353 return;
355 String stringToReplace = findManager.getStringToReplace(foundString.toString(), replaceContext.getFindModel());
356 if (stringToReplace != null) {
357 document.replaceString(textOffset, textEndOffset, stringToReplace);
364 private void doReplaceSelected(final ReplaceContext replaceContext) {
365 final Set<Usage> selectedUsages = replaceContext.getUsageView().getSelectedUsages();
366 if(selectedUsages == null){
367 return;
370 Set<VirtualFile> readOnlyFiles = null;
371 for (final Usage usage : selectedUsages) {
372 final VirtualFile file = ((UsageInFile)usage).getFile();
374 if (!file.isWritable()) {
375 if (readOnlyFiles == null) readOnlyFiles = new HashSet<VirtualFile>();
376 readOnlyFiles.add(file);
380 if (readOnlyFiles != null) {
381 ReadonlyStatusHandler.getInstance(myProject).ensureFilesWritable(VfsUtil.toVirtualFileArray(readOnlyFiles));
384 if (FindInProjectUtil.hasReadOnlyUsages(selectedUsages)){
385 int result = Messages.showOkCancelDialog(
386 replaceContext.getUsageView().getComponent(),
387 FindBundle.message("find.replace.occurrences.in.read.only.files.prompt"),
388 FindBundle.message("find.replace.occurrences.in.read.only.files.title"),
389 Messages.getWarningIcon()
391 if (result != 0){
392 return;
396 CommandProcessor.getInstance().executeCommand(
397 myProject, new Runnable() {
398 public void run() {
399 doReplace(replaceContext, selectedUsages);
400 for (final Usage selectedUsage : selectedUsages) {
401 replaceContext.getUsageView().removeUsage(selectedUsage);
404 if (replaceContext.getUsageView().getUsages().isEmpty()){
405 replaceContext.getUsageView().close();
406 return;
408 replaceContext.getUsageView().getComponent().requestFocus();
411 FindBundle.message("find.replace.command"),
412 null
416 public boolean isWorkInProgress() {
417 return myIsFindInProgress;
420 public boolean isEnabled () {
421 return !myIsFindInProgress && !FindInProjectManager.getInstance(myProject).isWorkInProgress();