update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / move / moveClassesOrPackages / MoveClassesOrPackagesImpl.java
blob8f69918665b5e85fec58dc62991f3d3d6c0faaf3
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 /**
18 * created at Nov 27, 2001
19 * @author Jeka
21 package com.intellij.refactoring.move.moveClassesOrPackages;
23 import com.intellij.history.LocalHistory;
24 import com.intellij.history.LocalHistoryAction;
25 import com.intellij.ide.util.DirectoryChooser;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.command.CommandProcessor;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.roots.ProjectRootManager;
31 import com.intellij.openapi.ui.Messages;
32 import com.intellij.openapi.util.Ref;
33 import com.intellij.openapi.vfs.VirtualFile;
34 import com.intellij.psi.*;
35 import com.intellij.refactoring.HelpID;
36 import com.intellij.refactoring.JavaRefactoringSettings;
37 import com.intellij.refactoring.PackageWrapper;
38 import com.intellij.refactoring.RefactoringBundle;
39 import com.intellij.refactoring.move.MoveCallback;
40 import com.intellij.refactoring.rename.DirectoryAsPackageRenameHandler;
41 import com.intellij.refactoring.rename.RenameUtil;
42 import com.intellij.refactoring.util.CommonRefactoringUtil;
43 import com.intellij.refactoring.util.RefactoringUIUtil;
44 import com.intellij.refactoring.util.RefactoringUtil;
45 import com.intellij.refactoring.util.TextOccurrencesUtil;
46 import com.intellij.util.IncorrectOperationException;
47 import org.jetbrains.annotations.Nullable;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
53 public class MoveClassesOrPackagesImpl {
54 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl");
56 public static void doMove(final Project project,
57 PsiElement[] elements,
58 PsiElement initialTargetElement,
59 final MoveCallback moveCallback) {
60 final PsiElement[] psiElements = adjustForMove(project, elements, initialTargetElement);
61 if (psiElements == null) {
62 return;
65 if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, Arrays.asList(psiElements), true)) {
66 return;
69 final String initialTargetPackageName = getInitialTargetPackageName(initialTargetElement, psiElements);
70 final PsiDirectory initialTargetDirectory = getInitialTargetDirectory(initialTargetElement, psiElements);
71 final boolean isTargetDirectoryFixed = initialTargetDirectory == null;
73 boolean searchTextOccurences = false;
74 for (int i = 0; i < psiElements.length && !searchTextOccurences; i++) {
75 PsiElement psiElement = psiElements[i];
76 searchTextOccurences = TextOccurrencesUtil.isSearchTextOccurencesEnabled(psiElement);
78 final MoveClassesOrPackagesDialog moveDialog =
79 new MoveClassesOrPackagesDialog(project, searchTextOccurences, psiElements, initialTargetElement, moveCallback);
80 boolean searchInComments = JavaRefactoringSettings.getInstance().MOVE_SEARCH_IN_COMMENTS;
81 boolean searchForTextOccurences = JavaRefactoringSettings.getInstance().MOVE_SEARCH_FOR_TEXT;
82 moveDialog.setData(psiElements, initialTargetPackageName, initialTargetDirectory, isTargetDirectoryFixed, searchInComments,
83 searchForTextOccurences, HelpID.getMoveHelpID(psiElements[0]));
84 moveDialog.show();
87 @Nullable
88 public static PsiElement[] adjustForMove(final Project project, final PsiElement[] elements, final PsiElement targetElement) {
89 final PsiElement[] psiElements = new PsiElement[elements.length];
90 List<String> names = new ArrayList<String>();
91 for (int idx = 0; idx < elements.length; idx++) {
92 PsiElement element = elements[idx];
93 if (element instanceof PsiDirectory) {
94 PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)element);
95 LOG.assertTrue(aPackage != null);
96 if (aPackage.getQualifiedName().length() == 0) { //is default package
97 String message = RefactoringBundle.message("move.package.refactoring.cannot.be.applied.to.default.package");
98 CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
99 return null;
101 if (!checkNesting(project, aPackage, targetElement)) return null;
102 if (!checkMovePackage(project, aPackage)) return null;
103 element = aPackage;
105 else if (element instanceof PsiPackage) {
106 final PsiPackage psiPackage = (PsiPackage)element;
107 if (!checkNesting(project, psiPackage, targetElement)) return null;
108 if (!checkMovePackage(project, psiPackage)) return null;
110 else if (element instanceof PsiClass) {
111 PsiClass aClass = (PsiClass)element;
112 if (aClass instanceof PsiAnonymousClass) {
113 String message = RefactoringBundle.message("move.class.refactoring.cannot.be.applied.to.anonymous.classes");
114 CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
115 return null;
117 if (!(aClass.getParent() instanceof PsiFile)) {
118 String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("moving.local.classes.is.not.supported"));
119 CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
120 return null;
123 String name = null;
124 for (MoveClassHandler nameProvider : MoveClassHandler.EP_NAME.getExtensions()) {
125 name = nameProvider.getName(aClass);
126 if (name != null) break;
128 if (name == null) name = aClass.getContainingFile().getName();
130 if (names.contains(name)) {
131 String message = RefactoringBundle
132 .getCannotRefactorMessage(RefactoringBundle.message("there.are.going.to.be.multiple.destination.files.with.the.same.name"));
133 CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, HelpID.getMoveHelpID(element), project);
134 return null;
137 names.add(name);
139 psiElements[idx] = element;
142 return psiElements;
145 private static boolean checkMovePackage(Project project, PsiPackage aPackage) {
146 final PsiDirectory[] directories = aPackage.getDirectories();
147 final VirtualFile[] virtualFiles = aPackage.occursInPackagePrefixes();
148 if (directories.length > 1 || virtualFiles.length > 0) {
149 final StringBuffer message = new StringBuffer();
150 RenameUtil.buildPackagePrefixChangedMessage(virtualFiles, message, aPackage.getQualifiedName());
151 if (directories.length > 1) {
152 DirectoryAsPackageRenameHandler.buildMultipleDirectoriesInPackageMessage(message, aPackage, directories);
153 message.append("\n\n");
154 String report = RefactoringBundle
155 .message("all.these.directories.will.be.moved.and.all.references.to.0.will.be.changed", aPackage.getQualifiedName());
156 message.append(report);
158 message.append("\n");
159 message.append(RefactoringBundle.message("do.you.wish.to.continue"));
160 int ret =
161 Messages.showYesNoDialog(project, message.toString(), RefactoringBundle.message("warning.title"), Messages.getWarningIcon());
162 if (ret != 0) {
163 return false;
166 return true;
169 private static boolean checkNesting(final Project project, final PsiPackage srcPackage, final PsiElement targetElement) {
170 final PsiPackage targetPackage = targetElement instanceof PsiPackage
171 ? (PsiPackage)targetElement
172 : targetElement instanceof PsiDirectory ? JavaDirectoryService.getInstance()
173 .getPackage((PsiDirectory)targetElement) : null;
174 for (PsiPackage curPackage = targetPackage; curPackage != null; curPackage = curPackage.getParentPackage()) {
175 if (curPackage.equals(srcPackage)) {
176 CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"),
177 RefactoringBundle.message("cannot.move.package.into.itself"),
178 HelpID.getMoveHelpID(srcPackage), project);
179 return false;
182 return true;
185 public static String getInitialTargetPackageName(PsiElement initialTargetElement, final PsiElement[] movedElements) {
186 String name = getContainerPackageName(initialTargetElement);
187 if (name == null) {
188 if (movedElements != null) {
189 name = getTargetPackageNameForMovedElement(movedElements[0]);
191 if (name == null) {
192 final PsiDirectory commonDirectory = getCommonDirectory(movedElements);
193 if (commonDirectory != null && JavaDirectoryService.getInstance().getPackage(commonDirectory) != null) {
194 name = JavaDirectoryService.getInstance().getPackage(commonDirectory).getQualifiedName();
198 if (name == null) {
199 name = "";
201 return name;
204 @Nullable
205 private static PsiDirectory getCommonDirectory(PsiElement[] movedElements) {
206 PsiDirectory commonDirectory = null;
208 for (PsiElement movedElement : movedElements) {
209 final PsiFile containingFile = movedElement.getContainingFile();
210 if (containingFile != null) {
211 final PsiDirectory containingDirectory = containingFile.getContainingDirectory();
212 if (containingDirectory != null) {
213 if (commonDirectory == null) {
214 commonDirectory = containingDirectory;
216 else {
217 if (commonDirectory != containingDirectory) {
218 return null;
224 if (commonDirectory != null) {
225 return commonDirectory;
227 else {
228 return null;
232 private static String getContainerPackageName(final PsiElement psiElement) {
233 if (psiElement instanceof PsiPackage) {
234 return ((PsiPackage)psiElement).getQualifiedName();
236 else if (psiElement instanceof PsiDirectory) {
237 PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)psiElement);
238 return aPackage != null ? aPackage.getQualifiedName() : "";
240 else if (psiElement != null) {
241 PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiElement.getContainingFile().getContainingDirectory());
242 return aPackage != null ? aPackage.getQualifiedName() : "";
244 else {
245 return null;
249 private static String getTargetPackageNameForMovedElement(final PsiElement psiElement) {
250 if (psiElement instanceof PsiPackage) {
251 final PsiPackage psiPackage = (PsiPackage)psiElement;
252 final PsiPackage parentPackage = psiPackage.getParentPackage();
253 return parentPackage != null ? parentPackage.getQualifiedName() : "";
255 else if (psiElement instanceof PsiDirectory) {
256 PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)psiElement);
257 return aPackage != null ? getTargetPackageNameForMovedElement(aPackage) : "";
259 else if (psiElement != null) {
260 PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(psiElement.getContainingFile().getContainingDirectory());
261 return aPackage != null ? aPackage.getQualifiedName() : "";
263 else {
264 return null;
269 public static PsiDirectory getInitialTargetDirectory(PsiElement initialTargetElement, final PsiElement[] movedElements) {
270 PsiDirectory initialTargetDirectory = getContainerDirectory(initialTargetElement);
271 if (initialTargetDirectory == null) {
272 if (movedElements != null) {
273 final PsiDirectory commonDirectory = getCommonDirectory(movedElements);
274 if (commonDirectory != null) {
275 initialTargetDirectory = commonDirectory;
277 else {
278 initialTargetDirectory = getContainerDirectory(movedElements[0]);
282 return initialTargetDirectory;
285 @Nullable
286 public static PsiDirectory getContainerDirectory(final PsiElement psiElement) {
287 if (psiElement instanceof PsiPackage) {
288 final PsiDirectory[] directories = ((PsiPackage)psiElement).getDirectories();
289 return directories.length == 1 ? directories[0] : null; //??
291 if (psiElement instanceof PsiDirectory) {
292 return (PsiDirectory)psiElement;
294 if (psiElement != null) {
295 return psiElement.getContainingFile().getContainingDirectory();
297 return null;
300 public static void doRearrangePackage(final Project project, final PsiDirectory[] directories) {
301 if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, Arrays.asList(directories), true)) {
302 return;
305 List<PsiDirectory> sourceRootDirectories = buildRearrangeTargetsList(project, directories);
306 DirectoryChooser chooser = new DirectoryChooser(project);
307 chooser.setTitle(RefactoringBundle.message("select.source.root.chooser.title"));
308 chooser.fillList(sourceRootDirectories.toArray(new PsiDirectory[sourceRootDirectories.size()]), null, project, "");
309 chooser.show();
310 if (!chooser.isOK()) return;
311 final PsiDirectory selectedTarget = chooser.getSelectedDirectory();
312 if (selectedTarget == null) return;
313 final Ref<IncorrectOperationException> ex = Ref.create(null);
314 final String commandDescription = RefactoringBundle.message("moving.directories.command");
315 Runnable runnable = new Runnable() {
316 public void run() {
317 ApplicationManager.getApplication().runWriteAction(new Runnable() {
318 public void run() {
319 LocalHistoryAction a = LocalHistory.startAction(project, commandDescription);
320 try {
321 rearrangeDirectoriesToTarget(directories, selectedTarget);
323 catch (IncorrectOperationException e) {
324 ex.set(e);
326 finally {
327 a.finish();
333 CommandProcessor.getInstance().executeCommand(project, runnable, commandDescription, null);
334 if (ex.get() != null) {
335 RefactoringUIUtil.processIncorrectOperation(project, ex.get());
339 private static List<PsiDirectory> buildRearrangeTargetsList(final Project project, final PsiDirectory[] directories) {
340 final VirtualFile[] sourceRoots = ProjectRootManager.getInstance(project).getContentSourceRoots();
341 List<PsiDirectory> sourceRootDirectories = new ArrayList<PsiDirectory>();
342 sourceRoots:
343 for (final VirtualFile sourceRoot : sourceRoots) {
344 PsiDirectory sourceRootDirectory = PsiManager.getInstance(project).findDirectory(sourceRoot);
345 if (sourceRootDirectory == null) continue;
346 final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(sourceRootDirectory);
347 if (aPackage == null) continue;
348 final String packagePrefix = aPackage.getQualifiedName();
349 for (final PsiDirectory directory : directories) {
350 String qualifiedName = JavaDirectoryService.getInstance().getPackage(directory).getQualifiedName();
351 if (!qualifiedName.startsWith(packagePrefix)) {
352 continue sourceRoots;
355 sourceRootDirectories.add(sourceRootDirectory);
357 return sourceRootDirectories;
360 private static void rearrangeDirectoriesToTarget(PsiDirectory[] directories, PsiDirectory selectedTarget)
361 throws IncorrectOperationException {
362 final VirtualFile sourceRoot = selectedTarget.getVirtualFile();
363 for (PsiDirectory directory : directories) {
364 final PsiPackage parentPackage = JavaDirectoryService.getInstance().getPackage(directory).getParentPackage();
365 final PackageWrapper wrapper = new PackageWrapper(parentPackage);
366 final PsiDirectory moveTarget = RefactoringUtil.createPackageDirectoryInSourceRoot(wrapper, sourceRoot);
367 MoveClassesOrPackagesUtil.moveDirectoryRecursively(directory, moveTarget);