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
;
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
;
66 myParameter
= parameter
;
67 myInitializer
= initializer
;
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
);
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);
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());
111 if (ApplicationManager
.getApplication().isUnitTestMode()) {
112 createLocal
= myMethod
.getProject().getUserData(CREATE_LOCAL_FOR_TESTS
);
115 InlineParameterDialog dlg
=
116 new InlineParameterDialog(InlineParameterHandler
.REFACTORING_NAME
, question
, HelpID
.INLINE_VARIABLE
, "OptionPane.questionIcon",
117 true, myMethod
.getProject());
118 if (!dlg
.showDialog()) {
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
);
149 final Map
<PsiElement
, PsiElement
> elementsToReplace
= new HashMap
<PsiElement
, PsiElement
>();
150 PsiExpression replacedInitializer
= (PsiExpression
)localInitializer
.copy();
151 if (replaceLocals(replacedInitializer
, elementsToReplace
)) {
153 replacedInitializer
= (PsiExpression
) RefactoringUtil
.replaceElementsWithMap(replacedInitializer
, elementsToReplace
);
155 catch (IncorrectOperationException 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();
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
);
191 final PsiLocalVariable declaredVar
= (PsiLocalVariable
) localDeclaration
.getDeclaredElements()[0];
192 PsiUtil
.setModifierProperty(declaredVar
, PsiModifier
.FINAL
, parameterIsFinal
);
193 final PsiCodeBlock body
= myMethod
.getBody();
195 body
.addAfter(localDeclaration
, body
.getLBrace());
199 for(PsiVariable var
: myLocalReplacements
.keySet()) {
200 if (ReferencesSearch
.search(var
).findFirst() == null) {
206 protected UndoConfirmationPolicy
getUndoConfirmationPolicy() {
207 return UndoConfirmationPolicy
.DEFAULT
;
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 return refCannotEvaluate
.isNull();
227 private boolean canEvaluate(final PsiReferenceExpression expression
,
228 final PsiElement element
,
229 final Map
<PsiElement
, PsiElement
> elementsToReplace
) {
230 if (element
instanceof PsiLocalVariable
|| element
instanceof PsiParameter
) {
231 final PsiVariable localVariable
= (PsiVariable
)element
;
232 final PsiElement localReplacement
= myLocalReplacements
.get(localVariable
);
233 if (localReplacement
!= null) {
234 elementsToReplace
.put(expression
, localReplacement
);
238 else if (element
instanceof PsiMethod
|| element
instanceof PsiField
) {
239 return mySameClass
|| ((PsiModifierListOwner
) element
).hasModifierProperty(PsiModifier
.STATIC
);
241 else if (element
instanceof PsiClass
) {