update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / inline / InlineMethodHandler.java
blobc3c0d54f798c0ab3a298c2c7a855fd04cc115e14
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.
17 package com.intellij.refactoring.inline;
19 import com.intellij.codeInsight.TargetElementUtilBase;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.progress.ProgressManager;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.psi.*;
26 import com.intellij.psi.search.searches.ReferencesSearch;
27 import com.intellij.psi.util.PsiTreeUtil;
28 import com.intellij.refactoring.HelpID;
29 import com.intellij.refactoring.RefactoringBundle;
30 import com.intellij.refactoring.util.CommonRefactoringUtil;
31 import com.intellij.refactoring.util.RefactoringUtil;
32 import com.intellij.util.Processor;
33 import com.intellij.lang.StdLanguages;
35 import java.util.ArrayList;
36 import java.util.List;
38 class InlineMethodHandler extends JavaInlineActionHandler {
39 private static final String REFACTORING_NAME = RefactoringBundle.message("inline.method.title");
41 private InlineMethodHandler() {
44 public boolean canInlineElement(PsiElement element) {
45 return element instanceof PsiMethod && element.getNavigationElement() instanceof PsiMethod && element.getLanguage() == StdLanguages.JAVA;
48 public void inlineElement(final Project project, Editor editor, PsiElement element) {
49 PsiMethod method = (PsiMethod)element.getNavigationElement();
50 if (method.getBody() == null){
51 String message;
52 if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
53 message = RefactoringBundle.message("refactoring.cannot.be.applied.to.abstract.methods", REFACTORING_NAME);
55 else {
56 message = RefactoringBundle.message("refactoring.cannot.be.applied.no.sources.attached", REFACTORING_NAME);
58 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_METHOD);
59 return;
62 PsiReference reference = editor != null ? TargetElementUtilBase.findReference(editor, editor.getCaretModel().getOffset()) : null;
63 boolean allowInlineThisOnly = false;
64 if (InlineMethodProcessor.checkBadReturns(method) && !allUsagesAreTailCalls(method)) {
65 if (reference != null && getTailCallType(reference) != TailCallType.None) {
66 allowInlineThisOnly = true;
68 else {
69 String message = RefactoringBundle.message("refactoring.is.not.supported.when.return.statement.interrupts.the.execution.flow", REFACTORING_NAME);
70 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_METHOD);
71 return;
75 if (reference == null && checkRecursive(method)) {
76 String message = RefactoringBundle.message("refactoring.is.not.supported.for.recursive.methods", REFACTORING_NAME);
77 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_METHOD);
78 return;
81 if (method.isConstructor()) {
82 if (method.isVarArgs()) {
83 String message = RefactoringBundle.message("refactoring.cannot.be.applied.to.vararg.constructors", REFACTORING_NAME);
84 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_CONSTRUCTOR);
85 return;
87 if (!isChainingConstructor(method)) {
88 String message = RefactoringBundle.message("refactoring.cannot.be.applied.to.inline.non.chaining.constructors", REFACTORING_NAME);
89 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_CONSTRUCTOR);
90 return;
92 if (reference != null) {
93 final PsiElement refElement = reference.getElement();
94 PsiCall constructorCall = refElement instanceof PsiJavaCodeReferenceElement ? RefactoringUtil.getEnclosingConstructorCall((PsiJavaCodeReferenceElement)refElement) : null;
95 if (constructorCall == null || !method.equals(constructorCall.resolveMethod())) reference = null;
98 else {
99 if (reference != null && !method.getManager().areElementsEquivalent(method, reference.resolve())) {
100 reference = null;
104 final boolean invokedOnReference = reference != null;
105 if (!invokedOnReference) {
106 final VirtualFile vFile = method.getContainingFile().getVirtualFile();
107 ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(vFile);
109 PsiJavaCodeReferenceElement refElement = reference != null ? (PsiJavaCodeReferenceElement)reference.getElement() : null;
110 InlineMethodDialog dialog = new InlineMethodDialog(project, method, refElement, editor, allowInlineThisOnly);
111 dialog.show();
114 public static boolean allUsagesAreTailCalls(final PsiMethod method) {
115 final List<PsiReference> nonTailCallUsages = new ArrayList<PsiReference>();
116 boolean result = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
117 public void run() {
118 ReferencesSearch.search(method).forEach(new Processor<PsiReference>() {
119 public boolean process(final PsiReference psiReference) {
120 ProgressManager.getInstance().checkCanceled();
121 if (getTailCallType(psiReference) == TailCallType.None) {
122 nonTailCallUsages.add(psiReference);
123 return false;
125 return true;
129 }, RefactoringBundle.message("inline.method.checking.tail.calls.progress"), true, method.getProject());
130 return result && nonTailCallUsages.isEmpty();
133 public enum TailCallType {
134 None, Simple, Return
137 public static TailCallType getTailCallType(final PsiReference psiReference) {
138 PsiElement element = psiReference.getElement();
139 PsiExpression methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
140 if (methodCall == null) return TailCallType.None;
141 if (methodCall.getParent() instanceof PsiReturnStatement) return TailCallType.Return;
142 if (methodCall.getParent() instanceof PsiExpressionStatement) {
143 PsiStatement callStatement = (PsiStatement) methodCall.getParent();
144 PsiMethod callerMethod = PsiTreeUtil.getParentOfType(callStatement, PsiMethod.class);
145 if (callerMethod != null) {
146 final PsiStatement[] psiStatements = callerMethod.getBody().getStatements();
147 return psiStatements.length > 0 && callStatement == psiStatements [psiStatements.length-1] ? TailCallType.Simple : TailCallType.None;
150 return TailCallType.None;
153 public static boolean isChainingConstructor(PsiMethod constructor) {
154 PsiCodeBlock body = constructor.getBody();
155 if (body != null) {
156 PsiStatement[] statements = body.getStatements();
157 if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) {
158 PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
159 if (expression instanceof PsiMethodCallExpression) {
160 PsiReferenceExpression methodExpr = ((PsiMethodCallExpression)expression).getMethodExpression();
161 if ("this".equals(methodExpr.getReferenceName())) {
162 PsiElement resolved = methodExpr.resolve();
163 return resolved instanceof PsiMethod && ((PsiMethod)resolved).isConstructor(); //delegated via "this" call
168 return false;
171 public static boolean checkRecursive(PsiMethod method) {
172 return checkCalls(method.getBody(), method);
175 private static boolean checkCalls(PsiElement scope, PsiMethod method) {
176 if (scope instanceof PsiMethodCallExpression){
177 PsiMethod refMethod = (PsiMethod)((PsiMethodCallExpression)scope).getMethodExpression().resolve();
178 if (method.equals(refMethod)) return true;
181 for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){
182 if (checkCalls(child, method)) return true;
185 return false;