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.
18 * created at Nov 27, 2001
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) {
65 if (!CommonRefactoringUtil
.checkReadOnlyStatusRecursively(project
, Arrays
.asList(psiElements
), true)) {
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]));
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
);
101 if (!checkNesting(project
, aPackage
, targetElement
)) return null;
102 if (!checkMovePackage(project
, aPackage
)) return null;
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
);
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
);
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
);
139 psiElements
[idx
] = element
;
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"));
161 Messages
.showYesNoDialog(project
, message
.toString(), RefactoringBundle
.message("warning.title"), Messages
.getWarningIcon());
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
);
185 public static String
getInitialTargetPackageName(PsiElement initialTargetElement
, final PsiElement
[] movedElements
) {
186 String name
= getContainerPackageName(initialTargetElement
);
188 if (movedElements
!= null) {
189 name
= getTargetPackageNameForMovedElement(movedElements
[0]);
192 final PsiDirectory commonDirectory
= getCommonDirectory(movedElements
);
193 if (commonDirectory
!= null && JavaDirectoryService
.getInstance().getPackage(commonDirectory
) != null) {
194 name
= JavaDirectoryService
.getInstance().getPackage(commonDirectory
).getQualifiedName();
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
;
217 if (commonDirectory
!= containingDirectory
) {
224 if (commonDirectory
!= null) {
225 return commonDirectory
;
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() : "";
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() : "";
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
;
278 initialTargetDirectory
= getContainerDirectory(movedElements
[0]);
282 return initialTargetDirectory
;
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();
300 public static void doRearrangePackage(final Project project
, final PsiDirectory
[] directories
) {
301 if (!CommonRefactoringUtil
.checkReadOnlyStatusRecursively(project
, Arrays
.asList(directories
), true)) {
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
, "");
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() {
317 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
319 LocalHistoryAction a
= LocalHistory
.startAction(project
, commandDescription
);
321 rearrangeDirectoriesToTarget(directories
, selectedTarget
);
323 catch (IncorrectOperationException e
) {
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
>();
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
);