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
.introduceField
;
18 import com
.intellij
.codeInsight
.AnnotationUtil
;
19 import com
.intellij
.codeInsight
.CodeInsightUtil
;
20 import com
.intellij
.codeInsight
.TestUtil
;
21 import com
.intellij
.openapi
.application
.ApplicationManager
;
22 import com
.intellij
.openapi
.command
.CommandProcessor
;
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
.psi
.*;
27 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
28 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
29 import com
.intellij
.psi
.search
.GlobalSearchScope
;
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 static com
.intellij
.refactoring
.introduceField
.BaseExpressionToFieldHandler
.InitializationPlace
.IN_CONSTRUCTOR
;
36 import static com
.intellij
.refactoring
.introduceField
.BaseExpressionToFieldHandler
.InitializationPlace
.IN_FIELD_DECLARATION
;
37 import com
.intellij
.refactoring
.ui
.TypeSelectorManagerImpl
;
38 import com
.intellij
.refactoring
.util
.CommonRefactoringUtil
;
39 import com
.intellij
.refactoring
.util
.EnumConstantsUtil
;
40 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
41 import com
.intellij
.util
.IncorrectOperationException
;
42 import org
.jetbrains
.annotations
.NonNls
;
44 public class LocalToFieldHandler
{
45 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.introduceField.LocalToFieldHandler");
47 private static final String REFACTORING_NAME
= RefactoringBundle
.message("convert.local.to.field.title");
48 private final Project myProject
;
49 private final boolean myIsConstant
;
50 private final PsiManager myManager
;
52 public LocalToFieldHandler(Project project
, boolean isConstant
) {
54 myManager
= PsiManager
.getInstance(myProject
);
55 myIsConstant
= isConstant
;
58 protected BaseExpressionToFieldHandler
.Settings
showRefactoringDialog(PsiClass aClass
, PsiLocalVariable local
, PsiExpression
[] occurences
, boolean isStatic
) {
59 final String fieldName
;
60 final BaseExpressionToFieldHandler
.InitializationPlace initializerPlace
;
61 final boolean declareFinal
;
62 @Modifier String fieldVisibility
;
63 final TypeSelectorManagerImpl typeSelectorManager
= new TypeSelectorManagerImpl(myProject
, local
.getType(),
67 final boolean annotateAsNonNls
;
68 final boolean introduceEnumConstant
;
70 IntroduceConstantDialog dialog
= new IntroduceConstantDialog(myProject
, aClass
,
71 local
.getInitializer(), local
, true, occurences
, aClass
, typeSelectorManager
74 if (!dialog
.isOK()) return null;
75 fieldName
= dialog
.getEnteredName();
77 initializerPlace
= IN_FIELD_DECLARATION
;
78 fieldVisibility
= dialog
.getFieldVisibility();
79 annotateAsNonNls
= dialog
.isAnnotateAsNonNls();
80 introduceEnumConstant
= dialog
.introduceEnumConstant();
83 PsiMethod method
= PsiTreeUtil
.getParentOfType(local
, PsiMethod
.class);
84 IntroduceFieldDialog dialog
= new IntroduceFieldDialog(myProject
, aClass
,
85 local
.getInitializer(), local
,
86 method
!= null && method
.isConstructor(),
88 occurences
.length
, method
!= null, method
!= null,
92 if (!dialog
.isOK()) return null;
93 fieldName
= dialog
.getEnteredName();
94 initializerPlace
= dialog
.getInitializerPlace();
95 declareFinal
= dialog
.isDeclareFinal();
96 fieldVisibility
= dialog
.getFieldVisibility();
97 annotateAsNonNls
= false;
98 introduceEnumConstant
= false;
101 return new BaseExpressionToFieldHandler
.Settings(fieldName
, true, isStatic
, declareFinal
, initializerPlace
, fieldVisibility
, local
, null, true, new BaseExpressionToFieldHandler
.TargetDestination(aClass
), annotateAsNonNls
,
102 introduceEnumConstant
);
105 public boolean convertLocalToField(final PsiLocalVariable local
, final Editor editor
) {
107 boolean tempIsStatic
= myIsConstant
;
108 PsiElement parent
= local
.getParent();
111 if (parent
instanceof PsiClass
&& !(parent
instanceof PsiAnonymousClass
)) {
112 aClass
= (PsiClass
)parent
;
115 if (parent
instanceof PsiFile
&& JspPsiUtil
.isInJspFile(parent
)) {
116 String message
= RefactoringBundle
.message("error.not.supported.for.jsp", REFACTORING_NAME
);
117 CommonRefactoringUtil
.showErrorHint(myProject
, editor
, message
, REFACTORING_NAME
, HelpID
.LOCAL_TO_FIELD
);
120 if (parent
instanceof PsiModifierListOwner
&&((PsiModifierListOwner
)parent
).hasModifierProperty(PsiModifier
.STATIC
)) {
123 parent
= parent
.getParent();
126 final boolean isStatic
= tempIsStatic
;
128 PsiExpression
[] occurences
= CodeInsightUtil
.findReferenceExpressions(RefactoringUtil
.getVariableScope(local
),
131 if (editor
!= null) {
132 RefactoringUtil
.highlightAllOccurences(myProject
, occurences
, editor
);
135 final BaseExpressionToFieldHandler
.Settings settings
= showRefactoringDialog(aClass
, local
, occurences
, isStatic
);
136 if (settings
== null) return false;
137 //LocalToFieldDialog dialog = new LocalToFieldDialog(project, aClass, local, isStatic);
138 final String variableName
= local
.getName();
139 final String fieldName
= settings
.getFieldName();
140 final BaseExpressionToFieldHandler
.InitializationPlace initializerPlace
= settings
.getInitializerPlace();
141 final boolean declareFinal
= settings
.isDeclareFinal();
142 @Modifier final String fieldVisibility
= settings
.getFieldVisibility();
143 final PsiClass destinationClass
= settings
.getDestinationClass();
144 boolean rebindNeeded
= false;
145 if (destinationClass
!= null) {
146 aClass
= destinationClass
;
149 final boolean annotateAsNonNls
= settings
.isAnnotateAsNonNls();
151 final PsiClass aaClass
= aClass
;
152 final boolean rebindNeeded1
= rebindNeeded
;
153 final Runnable runnable
= new Runnable() {
156 final boolean rebindNeeded2
= !variableName
.equals(fieldName
) || rebindNeeded1
;
157 final PsiReference
[] refs
;
159 refs
= ReferencesSearch
.search(local
, GlobalSearchScope
.projectScope(myProject
), false).toArray(new PsiReference
[0]);
165 final PsiMethod enclosingConstructor
= BaseExpressionToFieldHandler
.getEnclosingConstructor(aaClass
, local
);
166 PsiField field
= settings
.isIntroduceEnumConstant() ? EnumConstantsUtil
.createEnumConstant(aaClass
, local
, fieldName
)
167 : createField(local
, fieldName
, initializerPlace
== IN_FIELD_DECLARATION
);
168 if (!settings
.isIntroduceEnumConstant()) {
170 PsiUtil
.setModifierProperty(field
, PsiModifier
.STATIC
, true);
173 PsiUtil
.setModifierProperty(field
, PsiModifier
.FINAL
, true);
175 if (annotateAsNonNls
) {
176 PsiAnnotation annotation
= JavaPsiFacade
.getInstance(local
.getProject()).getElementFactory().createAnnotationFromText("@" + AnnotationUtil
.NON_NLS
, field
);
177 field
.getModifierList().addAfter(annotation
, null);
179 PsiUtil
.setModifierProperty(field
, fieldVisibility
, true);
182 field
= (PsiField
)aaClass
.add(field
);
183 JavaCodeStyleManager
.getInstance(myProject
).shortenClassReferences(field
);
185 local
.normalizeDeclaration();
186 PsiDeclarationStatement declarationStatement
= (PsiDeclarationStatement
)local
.getParent();
187 final BaseExpressionToFieldHandler
.InitializationPlace finalInitializerPlace
;
188 if (local
.getInitializer() == null) {
189 finalInitializerPlace
= IN_FIELD_DECLARATION
;
192 finalInitializerPlace
= initializerPlace
;
194 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(myManager
.getProject()).getElementFactory();
196 switch (finalInitializerPlace
) {
197 case IN_FIELD_DECLARATION
:
198 declarationStatement
.delete();
201 case IN_CURRENT_METHOD
:
202 PsiStatement statement
= createAssignment(local
, fieldName
, factory
);
203 declarationStatement
.replace(statement
);
207 addInitializationToConstructors(local
, field
, enclosingConstructor
, factory
);
209 case IN_SETUP_METHOD
:
210 addInitializationToSetUp(local
, field
, factory
);
213 if (enclosingConstructor
!= null && initializerPlace
== IN_CONSTRUCTOR
) {
214 PsiStatement statement
= createAssignment(local
, fieldName
, factory
);
215 declarationStatement
.replace(statement
);
219 for (final PsiReference reference
: refs
) {
220 if (reference
!= null) {
221 //expr = RefactoringUtil.outermostParenthesizedExpression(expr);
222 RefactoringUtil
.replaceOccurenceWithFieldRef((PsiExpression
)reference
, field
, aaClass
);
223 //replaceOccurenceWithFieldRef((PsiExpression)reference, field, aaClass);
226 //RefactoringUtil.renameVariableReferences(local, pPrefix + fieldName, GlobalSearchScope.projectScope(myProject));
229 catch (IncorrectOperationException e
) {
234 CommandProcessor
.getInstance().executeCommand(myProject
, new Runnable() {
236 ApplicationManager
.getApplication().runWriteAction(runnable
);
238 }, REFACTORING_NAME
, null);
242 private PsiField
createField(PsiLocalVariable local
, String fieldName
, boolean includeInitializer
) {
243 @NonNls StringBuilder pattern
= new StringBuilder();
244 pattern
.append("private int ");
245 pattern
.append(fieldName
);
246 if (local
.getInitializer() == null) {
247 includeInitializer
= false;
249 if (includeInitializer
) {
250 pattern
.append("=0");
253 PsiElementFactory factory
= JavaPsiFacade
.getInstance(myManager
.getProject()).getElementFactory();
255 PsiField field
= factory
.createFieldFromText(pattern
.toString(), null);
256 field
= (PsiField
)CodeStyleManager
.getInstance(myProject
).reformat(field
);
258 field
.getTypeElement().replace(factory
.createTypeElement(local
.getType()));
259 if (includeInitializer
) {
260 PsiExpression initializer
=
261 RefactoringUtil
.convertInitializerToNormalExpression(local
.getInitializer(), local
.getType());
262 field
.getInitializer().replace(initializer
);
267 catch (IncorrectOperationException e
) {
273 private PsiStatement
createAssignment(PsiLocalVariable local
, String fieldname
, PsiElementFactory factory
) {
275 String pattern
= fieldname
+ "=0;";
276 PsiExpressionStatement statement
= (PsiExpressionStatement
)factory
.createStatementFromText(pattern
, null);
277 statement
= (PsiExpressionStatement
)CodeStyleManager
.getInstance(myProject
).reformat(statement
);
279 PsiAssignmentExpression expr
= (PsiAssignmentExpression
)statement
.getExpression();
280 final PsiExpression initializer
= RefactoringUtil
.convertInitializerToNormalExpression(local
.getInitializer(), local
.getType());
281 expr
.getRExpression().replace(initializer
);
285 catch (IncorrectOperationException e
) {
291 private void addInitializationToSetUp(final PsiLocalVariable local
, final PsiField field
, final PsiElementFactory factory
)
292 throws IncorrectOperationException
{
293 PsiMethod inClass
= TestUtil
.findSetUpMethod(field
.getContainingClass());
294 assert inClass
!= null;
295 PsiStatement assignment
= createAssignment(local
, field
.getName(), factory
);
296 final PsiCodeBlock body
= inClass
.getBody();
298 body
.add(assignment
);
302 private void addInitializationToConstructors(PsiLocalVariable local
, PsiField field
, PsiMethod enclosingConstructor
,
303 PsiElementFactory factory
) throws IncorrectOperationException
{
304 PsiClass aClass
= field
.getContainingClass();
305 PsiMethod
[] constructors
= aClass
.getConstructors();
306 PsiStatement assignment
= createAssignment(local
, field
.getName(), factory
);
307 boolean added
= false;
308 for (PsiMethod constructor
: constructors
) {
309 if (constructor
== enclosingConstructor
) continue;
310 PsiCodeBlock body
= constructor
.getBody();
311 if (body
== null) continue;
312 PsiStatement
[] statements
= body
.getStatements();
313 if (statements
.length
> 0) {
314 PsiStatement first
= statements
[0];
315 if (first
instanceof PsiExpressionStatement
) {
316 PsiExpression expression
= ((PsiExpressionStatement
)first
).getExpression();
317 if (expression
instanceof PsiMethodCallExpression
) {
318 @NonNls String text
= ((PsiMethodCallExpression
)expression
).getMethodExpression().getText();
319 if ("this".equals(text
)) {
325 body
.add(assignment
);
328 if (!added
&& enclosingConstructor
== null) {
329 PsiMethod constructor
= factory
.createConstructor();
330 constructor
.getBody().add(assignment
);
331 aClass
.add(constructor
);
334 if (enclosingConstructor
== null) local
.delete();