update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / introduceField / LocalToFieldHandler.java
blob9055a91074c5a8525367e5127173c849d75137db
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.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) {
53 myProject = project;
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(),
64 occurences
67 final boolean annotateAsNonNls;
68 final boolean introduceEnumConstant;
69 if (myIsConstant) {
70 IntroduceConstantDialog dialog = new IntroduceConstantDialog(myProject, aClass,
71 local.getInitializer(), local, true, occurences, aClass, typeSelectorManager
73 dialog.show();
74 if (!dialog.isOK()) return null;
75 fieldName = dialog.getEnteredName();
76 declareFinal = true;
77 initializerPlace = IN_FIELD_DECLARATION;
78 fieldVisibility = dialog.getFieldVisibility();
79 annotateAsNonNls = dialog.isAnnotateAsNonNls();
80 introduceEnumConstant = dialog.introduceEnumConstant();
82 else {
83 PsiMethod method = PsiTreeUtil.getParentOfType(local, PsiMethod.class);
84 IntroduceFieldDialog dialog = new IntroduceFieldDialog(myProject, aClass,
85 local.getInitializer(), local,
86 method != null && method.isConstructor(),
87 true, isStatic,
88 occurences.length, method != null, method != null,
89 typeSelectorManager
91 dialog.show();
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) {
106 PsiClass aClass;
107 boolean tempIsStatic = myIsConstant;
108 PsiElement parent = local.getParent();
110 while (true) {
111 if (parent instanceof PsiClass && !(parent instanceof PsiAnonymousClass)) {
112 aClass = (PsiClass)parent;
113 break;
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);
118 return false;
120 if (parent instanceof PsiModifierListOwner &&((PsiModifierListOwner)parent).hasModifierProperty(PsiModifier.STATIC)) {
121 tempIsStatic = true;
123 parent = parent.getParent();
126 final boolean isStatic = tempIsStatic;
128 PsiExpression[] occurences = CodeInsightUtil.findReferenceExpressions(RefactoringUtil.getVariableScope(local),
129 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;
147 rebindNeeded = true;
149 final boolean annotateAsNonNls = settings.isAnnotateAsNonNls();
151 final PsiClass aaClass = aClass;
152 final boolean rebindNeeded1 = rebindNeeded;
153 final Runnable runnable = new Runnable() {
154 public void run() {
155 try {
156 final boolean rebindNeeded2 = !variableName.equals(fieldName) || rebindNeeded1;
157 final PsiReference[] refs;
158 if (rebindNeeded2) {
159 refs = ReferencesSearch.search(local, GlobalSearchScope.projectScope(myProject), false).toArray(new PsiReference[0]);
161 else {
162 refs = null;
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()) {
169 if (isStatic) {
170 PsiUtil.setModifierProperty(field, PsiModifier.STATIC, true);
172 if (declareFinal) {
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;
191 else {
192 finalInitializerPlace = initializerPlace;
194 final PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
196 switch (finalInitializerPlace) {
197 case IN_FIELD_DECLARATION:
198 declarationStatement.delete();
199 break;
201 case IN_CURRENT_METHOD:
202 PsiStatement statement = createAssignment(local, fieldName, factory);
203 declarationStatement.replace(statement);
204 break;
206 case IN_CONSTRUCTOR:
207 addInitializationToConstructors(local, field, enclosingConstructor, factory);
208 break;
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);
218 if (rebindNeeded2) {
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) {
230 LOG.error(e);
234 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
235 public void run() {
236 ApplicationManager.getApplication().runWriteAction(runnable);
238 }, REFACTORING_NAME, null);
239 return true;
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");
252 pattern.append(";");
253 PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
254 try {
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);
265 return field;
267 catch (IncorrectOperationException e) {
268 LOG.error(e);
269 return null;
273 private PsiStatement createAssignment(PsiLocalVariable local, String fieldname, PsiElementFactory factory) {
274 try {
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);
283 return statement;
285 catch (IncorrectOperationException e) {
286 LOG.error(e);
287 return null;
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();
297 assert body != null;
298 body.add(assignment);
299 local.delete();
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)) {
320 continue;
325 body.add(assignment);
326 added = true;
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();