| /* |
| * 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. |
| */ |
| |
| /** |
| * created at Nov 26, 2001 |
| * @author Jeka |
| */ |
| package com.intellij.refactoring.move; |
| |
| import com.intellij.openapi.actionSystem.DataContext; |
| import com.intellij.openapi.actionSystem.LangDataKeys; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.ScrollType; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.psi.util.PsiUtilCore; |
| import com.intellij.refactoring.RefactoringActionHandler; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil; |
| import com.intellij.refactoring.util.CommonRefactoringUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| public class MoveHandler implements RefactoringActionHandler { |
| public static final String REFACTORING_NAME = RefactoringBundle.message("move.title"); |
| |
| /** |
| * called by an Action in AtomicAction when refactoring is invoked from Editor |
| */ |
| @Override |
| public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { |
| int offset = editor.getCaretModel().getOffset(); |
| editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); |
| PsiElement element = file.findElementAt(offset); |
| while(true){ |
| if (element == null) { |
| String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.at.the.class.method.or.field.to.be.refactored")); |
| CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, null); |
| return; |
| } |
| |
| if (tryToMoveElement(element, project, dataContext, null, editor)) { |
| return; |
| } |
| final TextRange range = element.getTextRange(); |
| if (range != null) { |
| int relative = offset - range.getStartOffset(); |
| final PsiReference reference = element.findReferenceAt(relative); |
| if (reference != null) { |
| final PsiElement refElement = reference.resolve(); |
| if (refElement != null && tryToMoveElement(refElement, project, dataContext, reference, editor)) return; |
| } |
| } |
| |
| element = element.getParent(); |
| } |
| } |
| |
| private static boolean tryToMoveElement(final PsiElement element, final Project project, final DataContext dataContext, |
| final PsiReference reference, final Editor editor) { |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.tryToMove(element, project, dataContext, reference, editor)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * called by an Action in AtomicAction |
| */ |
| @Override |
| public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { |
| final PsiElement targetContainer = dataContext == null ? null : LangDataKeys.TARGET_PSI_ELEMENT.getData(dataContext); |
| final Set<PsiElement> filesOrDirs = new HashSet<PsiElement>(); |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.canMove(dataContext) && delegate.isValidTarget(targetContainer, elements)) { |
| delegate.collectFilesOrDirsFromContext(dataContext, filesOrDirs); |
| } |
| } |
| if (!filesOrDirs.isEmpty()) { |
| for (PsiElement element : elements) { |
| if (element instanceof PsiDirectory) { |
| filesOrDirs.add(element); |
| } |
| else { |
| final PsiFile containingFile = element.getContainingFile(); |
| if (containingFile != null) { |
| filesOrDirs.add(containingFile); |
| } |
| } |
| } |
| MoveFilesOrDirectoriesUtil |
| .doMove(project, PsiUtilCore.toPsiElementArray(filesOrDirs), new PsiElement[]{targetContainer}, null); |
| return; |
| } |
| doMove(project, elements, targetContainer, dataContext, null); |
| } |
| |
| /** |
| * must be invoked in AtomicAction |
| */ |
| public static void doMove(Project project, @NotNull PsiElement[] elements, PsiElement targetContainer, DataContext dataContext, MoveCallback callback) { |
| if (elements.length == 0) return; |
| |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.canMove(elements, targetContainer)) { |
| delegate.doMove(project, elements, delegate.adjustTargetForMove(dataContext, targetContainer), callback); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Performs some extra checks (that canMove does not) |
| * May replace some elements with others which actulaly shall be moved (e.g. directory->package) |
| */ |
| @Nullable |
| public static PsiElement[] adjustForMove(Project project, final PsiElement[] sourceElements, final PsiElement targetElement) { |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.canMove(sourceElements, targetElement)) { |
| return delegate.adjustForMove(project, sourceElements, targetElement); |
| } |
| } |
| return sourceElements; |
| } |
| |
| /** |
| * Must be invoked in AtomicAction |
| * target container can be null => means that container is not determined yet and must be spacify by the user |
| */ |
| public static boolean canMove(@NotNull PsiElement[] elements, PsiElement targetContainer) { |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.canMove(elements, targetContainer)) return true; |
| } |
| |
| return false; |
| } |
| |
| public static boolean isValidTarget(final PsiElement psiElement, PsiElement[] elements) { |
| if (psiElement != null) { |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.isValidTarget(psiElement, elements)){ |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean canMove(DataContext dataContext) { |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.canMove(dataContext)) return true; |
| } |
| |
| return false; |
| } |
| |
| public static boolean isMoveRedundant(PsiElement source, PsiElement target) { |
| for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { |
| if (delegate.isMoveRedundant(source, target)) return true; |
| } |
| return false; |
| } |
| } |