inline parameter: evaluate this expression (IDEA-40666)
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / inline / InlineParameterExpressionProcessor.java
blob10e7b652c0b20488173e2325ce3b711e113689e3
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.inline;
18 import com.intellij.codeInspection.sameParameterValue.SameParameterValueInspection;
19 import com.intellij.openapi.application.Result;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.command.UndoConfirmationPolicy;
22 import com.intellij.openapi.command.WriteCommandAction;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.editor.Editor;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.Key;
27 import com.intellij.openapi.util.Ref;
28 import com.intellij.psi.*;
29 import com.intellij.psi.controlFlow.DefUseUtil;
30 import com.intellij.psi.search.searches.ReferencesSearch;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.psi.util.PsiUtil;
33 import com.intellij.refactoring.HelpID;
34 import com.intellij.refactoring.RefactoringBundle;
35 import com.intellij.refactoring.util.CommonRefactoringUtil;
36 import com.intellij.refactoring.util.InlineUtil;
37 import com.intellij.refactoring.util.RefactoringUtil;
38 import com.intellij.util.IncorrectOperationException;
40 import java.util.Collection;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.Map;
45 /**
46 * @author yole
48 public class InlineParameterExpressionProcessor {
49 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inline.InlineParameterExpressionProcessor");
51 private final PsiCallExpression myMethodCall;
52 private final PsiMethod myMethod;
53 private final PsiParameter myParameter;
54 private final PsiExpression myInitializer;
55 private final Editor myEditor;
56 private final boolean mySameClass;
57 private final PsiMethod myCallingMethod;
58 private Map<PsiVariable, PsiElement> myLocalReplacements;
60 public InlineParameterExpressionProcessor(final PsiCallExpression methodCall,
61 final PsiMethod method,
62 final PsiParameter parameter,
63 final PsiExpression initializer, Editor editor) {
64 myMethodCall = methodCall;
65 myMethod = method;
66 myParameter = parameter;
67 myInitializer = initializer;
68 myEditor = editor;
70 PsiClass callingClass = PsiTreeUtil.getParentOfType(methodCall, PsiClass.class);
71 mySameClass = (callingClass == myMethod.getContainingClass());
72 myCallingMethod = PsiTreeUtil.getParentOfType(myMethodCall, PsiMethod.class);
75 void run() throws IncorrectOperationException {
76 int parameterIndex = myMethod.getParameterList().getParameterIndex(myParameter);
77 myLocalReplacements = new HashMap<PsiVariable, PsiElement>();
78 final PsiExpression[] arguments = myMethodCall.getArgumentList().getExpressions();
79 for(int i=0; i<arguments.length; i++) {
80 if (i != parameterIndex && arguments [i] instanceof PsiReferenceExpression) {
81 final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)arguments[i];
82 final PsiElement element = referenceExpression.resolve();
83 if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
84 final PsiParameter param = myMethod.getParameterList().getParameters()[i];
85 final PsiExpression paramRef =
86 JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory().createExpressionFromText(param.getName(), myMethod);
87 myLocalReplacements.put((PsiVariable) element, paramRef);
92 processParameterInitializer();
94 PsiExpression initializerInMethod = (PsiExpression) myInitializer.copy();
95 final Map<PsiElement, PsiElement> elementsToReplace = new HashMap<PsiElement, PsiElement>();
96 final boolean canEvaluate = replaceLocals(initializerInMethod, elementsToReplace);
97 if (!canEvaluate) {
98 CommonRefactoringUtil.showErrorHint(myMethod.getProject(), myEditor,
99 "Parameter initializer depends on values which are not available inside the method and cannot be inlined",
100 RefactoringBundle.message("inline.parameter.refactoring"), null);
101 return;
104 final Collection<PsiReference> parameterRefs = ReferencesSearch.search(myParameter).findAll();
106 initializerInMethod = (PsiExpression) RefactoringUtil.replaceElementsWithMap(initializerInMethod, elementsToReplace);
108 String question = RefactoringBundle.message("inline.parameter.confirmation", myParameter.getName(),
109 initializerInMethod.getText());
110 boolean createLocal;
111 if (ApplicationManager.getApplication().isUnitTestMode()) {
112 createLocal = myMethod.getProject().getUserData(CREATE_LOCAL_FOR_TESTS);
114 else {
115 InlineParameterDialog dlg =
116 new InlineParameterDialog(InlineParameterHandler.REFACTORING_NAME, question, HelpID.INLINE_VARIABLE, "OptionPane.questionIcon",
117 true, myMethod.getProject());
118 if (!dlg.showDialog()) {
119 return;
121 createLocal = dlg.isCreateLocal();
123 performRefactoring(initializerInMethod, parameterRefs, createLocal);
125 public static final Key<Boolean> CREATE_LOCAL_FOR_TESTS = Key.create("CREATE_INLINE_PARAMETER_LOCAL_FOR_TESTS");
127 private void processParameterInitializer() {
128 myInitializer.accept(new JavaRecursiveElementVisitor() {
129 @Override public void visitReferenceExpression(final PsiReferenceExpression expression) {
130 super.visitReferenceExpression(expression);
131 final PsiElement element = expression.resolve();
132 if (element instanceof PsiLocalVariable) {
133 final PsiLocalVariable localVariable = (PsiLocalVariable)element;
134 if (myLocalReplacements.containsKey(localVariable)) return;
135 final PsiElement[] elements = DefUseUtil.getDefs(myCallingMethod.getBody(), localVariable, expression);
136 if (elements.length == 1) {
137 PsiExpression localInitializer = null;
138 if (elements [0] instanceof PsiLocalVariable) {
139 localInitializer = ((PsiLocalVariable) elements [0]).getInitializer();
141 else if (elements [0] instanceof PsiAssignmentExpression) {
142 localInitializer = ((PsiAssignmentExpression) elements [0]).getRExpression();
144 if (localInitializer != null) {
145 if (InlineToAnonymousConstructorProcessor.isConstant(localInitializer)) {
146 myLocalReplacements.put(localVariable, localInitializer);
148 else {
149 final Map<PsiElement, PsiElement> elementsToReplace = new HashMap<PsiElement, PsiElement>();
150 PsiExpression replacedInitializer = (PsiExpression)localInitializer.copy();
151 if (replaceLocals(replacedInitializer, elementsToReplace)) {
152 try {
153 replacedInitializer = (PsiExpression) RefactoringUtil.replaceElementsWithMap(replacedInitializer, elementsToReplace);
155 catch (IncorrectOperationException e) {
156 LOG.error(e);
158 myLocalReplacements.put(localVariable, replacedInitializer);
168 private void performRefactoring(final PsiExpression initializerInMethod, final Collection<PsiReference> parameterRefs,
169 final boolean createLocal) {
170 final Collection<PsiFile> containingFiles = new HashSet<PsiFile>();
171 containingFiles.add(myMethod.getContainingFile());
172 containingFiles.add(myMethodCall.getContainingFile());
174 final Project project = myMethod.getProject();
175 new WriteCommandAction(project,
176 RefactoringBundle.message("inline.parameter.command.name", myParameter.getName()),
177 containingFiles.toArray(new PsiFile[containingFiles.size()])) {
178 protected void run(final Result result) throws Throwable {
179 final PsiElementFactory factory = JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory();
180 if (!createLocal) {
181 for(PsiReference ref: parameterRefs) {
182 InlineUtil.inlineVariable(myParameter, initializerInMethod, (PsiJavaCodeReferenceElement) ref.getElement());
185 PsiDeclarationStatement localDeclaration = factory.createVariableDeclarationStatement(myParameter.getName(),
186 myParameter.getType(),
187 initializerInMethod);
188 boolean parameterIsFinal = myParameter.hasModifierProperty(PsiModifier.FINAL);
189 SameParameterValueInspection.InlineParameterValueFix.removeParameter(myMethod, myParameter);
190 if (createLocal) {
191 final PsiLocalVariable declaredVar = (PsiLocalVariable) localDeclaration.getDeclaredElements()[0];
192 PsiUtil.setModifierProperty(declaredVar, PsiModifier.FINAL, parameterIsFinal);
193 final PsiCodeBlock body = myMethod.getBody();
194 if (body != null) {
195 body.addAfter(localDeclaration, body.getLBrace());
199 for(PsiVariable var: myLocalReplacements.keySet()) {
200 if (ReferencesSearch.search(var).findFirst() == null) {
201 var.delete();
206 protected UndoConfirmationPolicy getUndoConfirmationPolicy() {
207 return UndoConfirmationPolicy.DEFAULT;
209 }.execute();
212 private boolean replaceLocals(final PsiExpression expression,
213 final Map<PsiElement, PsiElement> elementsToReplace) {
214 final Ref<Boolean> refCannotEvaluate = new Ref<Boolean>();
215 expression.accept(new JavaRecursiveElementWalkingVisitor() {
216 @Override public void visitReferenceExpression(final PsiReferenceExpression expression) {
217 super.visitReferenceExpression(expression);
218 final PsiElement element = expression.resolve();
219 if (!canEvaluate(expression, element, elementsToReplace)) {
220 refCannotEvaluate.set(Boolean.TRUE);
224 @Override
225 public void visitThisExpression(PsiThisExpression thisExpression) {
226 super.visitThisExpression(thisExpression);
227 final PsiJavaCodeReferenceElement qualifier = thisExpression.getQualifier();
228 PsiElement containingClass;
229 if (qualifier != null) {
230 containingClass = qualifier.resolve();
231 } else {
232 containingClass = PsiTreeUtil.getParentOfType(myMethodCall, PsiClass.class);
234 final PsiClass methodContainingClass = myMethod.getContainingClass();
235 LOG.assertTrue(methodContainingClass != null);
236 if (!PsiTreeUtil.isAncestor(containingClass, methodContainingClass, false)) {
237 refCannotEvaluate.set(Boolean.TRUE);
241 return refCannotEvaluate.isNull();
244 private boolean canEvaluate(final PsiReferenceExpression expression,
245 final PsiElement element,
246 final Map<PsiElement, PsiElement> elementsToReplace) {
247 if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
248 final PsiVariable localVariable = (PsiVariable)element;
249 final PsiElement localReplacement = myLocalReplacements.get(localVariable);
250 if (localReplacement != null) {
251 elementsToReplace.put(expression, localReplacement);
252 return true;
255 else if (element instanceof PsiMethod || element instanceof PsiField) {
256 return mySameClass || ((PsiModifierListOwner) element).hasModifierProperty(PsiModifier.STATIC);
258 else if (element instanceof PsiClass) {
259 return true;
261 return false;