update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / extractMethod / ExtractMethodHandler.java
blobac2a6f2927a96acf1d5c974c046d9b58b5be4355
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.
16 package com.intellij.refactoring.extractMethod;
18 import com.intellij.codeInsight.CodeInsightUtil;
19 import com.intellij.codeInsight.highlighting.HighlightManager;
20 import com.intellij.openapi.actionSystem.DataContext;
21 import com.intellij.openapi.actionSystem.LangDataKeys;
22 import com.intellij.openapi.actionSystem.PlatformDataKeys;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.command.CommandProcessor;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.editor.LogicalPosition;
28 import com.intellij.openapi.editor.ScrollType;
29 import com.intellij.openapi.editor.colors.EditorColors;
30 import com.intellij.openapi.editor.colors.EditorColorsManager;
31 import com.intellij.openapi.editor.markup.TextAttributes;
32 import com.intellij.openapi.fileEditor.FileEditorManager;
33 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.util.Pass;
36 import com.intellij.openapi.util.TextRange;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import com.intellij.openapi.wm.WindowManager;
39 import com.intellij.psi.*;
40 import com.intellij.psi.impl.source.PostprocessReformattingAspect;
41 import com.intellij.refactoring.HelpID;
42 import com.intellij.refactoring.RefactoringActionHandler;
43 import com.intellij.refactoring.RefactoringBundle;
44 import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
45 import com.intellij.refactoring.util.CommonRefactoringUtil;
46 import com.intellij.refactoring.util.RefactoringUtil;
47 import com.intellij.refactoring.util.duplicates.DuplicatesImpl;
48 import com.intellij.util.IncorrectOperationException;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
52 import java.util.List;
54 public class ExtractMethodHandler implements RefactoringActionHandler {
55 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractMethod.ExtractMethodHandler");
57 public static final String REFACTORING_NAME = RefactoringBundle.message("extract.method.title");
59 public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
60 if (dataContext != null) {
61 final PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext);
62 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
63 if (file != null && editor != null) {
64 invokeOnElements(project, editor, file, elements);
69 public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) {
70 final Pass<PsiElement[]> callback = new Pass<PsiElement[]>() {
71 public void pass(final PsiElement[] selectedValue) {
72 invokeOnElements(project, editor, file, selectedValue);
75 selectAndPass(project, editor, file, callback);
78 public static void selectAndPass(final Project project, final Editor editor, final PsiFile file, final Pass<PsiElement[]> callback) {
79 editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
80 if (!editor.getSelectionModel().hasSelection()) {
81 final int offset = editor.getCaretModel().getOffset();
82 final PsiElement[] statementsInRange = IntroduceVariableBase.findStatementsAtOffset(editor, file, offset);
83 final List<PsiExpression> expressions = IntroduceVariableBase.collectExpressions(file, editor, offset, statementsInRange);
84 if (expressions.size() < 2) {
85 editor.getSelectionModel().selectLineAtCaret();
87 else {
88 IntroduceVariableBase.showChooser(editor, expressions, new Pass<PsiExpression>() {
89 @Override
90 public void pass(PsiExpression psiExpression) {
91 callback.pass(new PsiExpression[]{psiExpression});
93 });
94 return;
98 int startOffset = editor.getSelectionModel().getSelectionStart();
99 int endOffset = editor.getSelectionModel().getSelectionEnd();
101 PsiDocumentManager.getInstance(project).commitAllDocuments();
103 PsiElement[] elements;
104 PsiExpression expr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset);
105 if (expr != null) {
106 elements = new PsiElement[]{expr};
108 else {
109 elements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
110 if (elements.length == 0) {
111 final PsiExpression expression = IntroduceVariableBase.getSelectedExpression(project, file, startOffset, endOffset);
112 if (expression != null) {
113 final PsiType originalType = RefactoringUtil.getTypeByExpressionWithExpectedType(expression);
114 if (originalType != null) {
115 elements = new PsiElement[]{expression};
120 callback.pass(elements);
123 private static void invokeOnElements(Project project, Editor editor, PsiFile file, PsiElement[] elements) {
124 final ExtractMethodProcessor processor = getProcessor(elements, project, file, editor, true);
125 if (processor != null) {
126 invokeOnElements(project, editor, processor, true);
130 private static boolean invokeOnElements(final Project project, final Editor editor, @NotNull final ExtractMethodProcessor processor, final boolean directTypes) {
131 if (!CommonRefactoringUtil.checkReadOnlyStatus(project, processor.getTargetClass().getContainingFile())) return false;
132 if (processor.showDialog(directTypes)) {
133 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
134 public void run() {
135 PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable() {
136 public void run() {
137 ApplicationManager.getApplication().runWriteAction(new Runnable() {
138 public void run() {
139 try {
140 processor.doRefactoring();
142 catch (IncorrectOperationException e) {
143 LOG.error(e);
147 DuplicatesImpl.processDuplicates(processor, project, editor);
151 }, REFACTORING_NAME, null);
152 return true;
154 return false;
157 @Nullable
158 private static ExtractMethodProcessor getProcessor(final PsiElement[] elements,
159 final Project project,
160 final PsiFile file,
161 final Editor editor,
162 final boolean showErrorMessages) {
163 if (elements == null || elements.length == 0) {
164 if (showErrorMessages) {
165 String message = RefactoringBundle
166 .getCannotRefactorMessage(RefactoringBundle.message("selected.block.should.represent.a.set.of.statements.or.an.expression"));
167 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.EXTRACT_METHOD);
169 return null;
172 for (PsiElement element : elements) {
173 if (element instanceof PsiStatement && RefactoringUtil.isSuperOrThisCall((PsiStatement)element, true, true)) {
174 if (showErrorMessages) {
175 String message = RefactoringBundle
176 .getCannotRefactorMessage(RefactoringBundle.message("selected.block.contains.invocation.of.another.class.constructor"));
177 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.EXTRACT_METHOD);
179 return null;
183 final ExtractMethodProcessor processor =
184 new ExtractMethodProcessor(project, editor, elements, null, REFACTORING_NAME, "", HelpID.EXTRACT_METHOD);
185 processor.setShowErrorDialogs(showErrorMessages);
186 try {
187 if (!processor.prepare()) return null;
189 catch (PrepareFailedException e) {
190 if (showErrorMessages) {
191 CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), REFACTORING_NAME, HelpID.EXTRACT_METHOD);
192 highlightPrepareError(e, file, editor, project);
194 return null;
196 return processor;
199 public static void highlightPrepareError(PrepareFailedException e, PsiFile file, Editor editor, final Project project) {
200 if (e.getFile() == file) {
201 final TextRange textRange = e.getTextRange();
202 final HighlightManager highlightManager = HighlightManager.getInstance(project);
203 EditorColorsManager colorsManager = EditorColorsManager.getInstance();
204 TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
205 highlightManager.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, null);
206 final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(textRange.getStartOffset());
207 editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.MAKE_VISIBLE);
208 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
212 @Nullable
213 public static ExtractMethodProcessor getProcessor(final Project project,
214 final PsiElement[] elements,
215 final PsiFile file,
216 final boolean openEditor) {
217 return getProcessor(elements, project, file, openEditor ? openEditor(project, file) : null, false);
220 public static boolean invokeOnElements(final Project project, @NotNull final ExtractMethodProcessor processor, final PsiFile file, final boolean directTypes) {
221 return invokeOnElements(project, openEditor(project, file), processor, directTypes);
224 @Nullable
225 private static Editor openEditor(final Project project, final PsiFile file) {
226 final VirtualFile virtualFile = file.getVirtualFile();
227 LOG.assertTrue(virtualFile != null);
228 final OpenFileDescriptor fileDescriptor = new OpenFileDescriptor(project, virtualFile);
229 return FileEditorManager.getInstance(project).openTextEditor(fileDescriptor, false);