update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / intention / impl / CreateFieldFromParameterAction.java
blob724c7e531bb182ac972f6879ce77ec2b9978696a
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.codeInsight.intention.impl;
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.codeInsight.CodeInsightBundle;
20 import com.intellij.codeInsight.CodeInsightUtilBase;
21 import com.intellij.codeInsight.intention.IntentionAction;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.editor.Editor;
25 import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.Pair;
28 import com.intellij.openapi.util.Ref;
29 import com.intellij.openapi.util.TextRange;
30 import com.intellij.psi.*;
31 import com.intellij.psi.codeStyle.CodeStyleManager;
32 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
33 import com.intellij.psi.codeStyle.SuggestedNameInfo;
34 import com.intellij.psi.codeStyle.VariableKind;
35 import com.intellij.psi.search.LocalSearchScope;
36 import com.intellij.psi.search.searches.ReferencesSearch;
37 import com.intellij.psi.util.PsiTreeUtil;
38 import com.intellij.util.IncorrectOperationException;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.List;
48 public class CreateFieldFromParameterAction implements IntentionAction {
49 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction");
50 private String myName = "";
52 @Nullable
53 private static PsiType getType(final PsiParameter parameter) {
54 if (parameter == null) return null;
55 PsiType type = parameter.getType();
56 if (type instanceof PsiEllipsisType) type = ((PsiEllipsisType)type).toArrayType();
57 return type;
60 @NotNull
61 public String getText() {
62 return CodeInsightBundle.message("intention.create.field.from.parameter.text", myName);
65 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
66 PsiParameter myParameter = findParameterAtCursor(file, editor);
67 if (myParameter == null) return false;
68 myName = myParameter.getName();
69 final PsiType type = getType(myParameter);
70 PsiClass targetClass = PsiTreeUtil.getParentOfType(myParameter, PsiClass.class);
71 return
72 myParameter.isValid()
73 && myParameter.getDeclarationScope() instanceof PsiMethod
74 && ((PsiMethod)myParameter.getDeclarationScope()).getBody() != null
75 && myParameter.getManager().isInProject(myParameter)
76 && type != null
77 && type.isValid()
78 && !isParameterAssignedToField(myParameter)
79 && targetClass != null
80 && !targetClass.isInterface()
84 static boolean isParameterAssignedToField(final PsiParameter parameter) {
85 for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) {
86 if (!(reference instanceof PsiReferenceExpression)) continue;
87 final PsiReferenceExpression expression = (PsiReferenceExpression)reference;
88 if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue;
89 final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent();
90 if (assignmentExpression.getRExpression() != expression) continue;
91 final PsiExpression lExpression = assignmentExpression.getLExpression();
92 if (!(lExpression instanceof PsiReferenceExpression)) continue;
93 final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
94 if (!(element instanceof PsiField)) continue;
95 return true;
97 return false;
100 @Nullable
101 static PsiParameter findParameterAtCursor(final PsiFile file, final Editor editor) {
102 final int offset = editor.getCaretModel().getOffset();
103 final PsiParameterList parameterList = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiParameterList.class, false);
104 if (parameterList == null) return null;
105 final PsiParameter[] parameters = parameterList.getParameters();
106 for (PsiParameter parameter : parameters) {
107 final TextRange range = parameter.getTextRange();
108 if (range.getStartOffset() <= offset && offset <= range.getEndOffset()) return parameter;
110 return null;
113 @NotNull
114 public String getFamilyName() {
115 return CodeInsightBundle.message("intention.create.field.from.parameter.family");
118 public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
119 invoke(project, editor, file, !ApplicationManager.getApplication().isUnitTestMode());
122 private static void invoke(final Project project, Editor editor, PsiFile file, boolean isInteractive) {
123 final PsiParameter myParameter = findParameterAtCursor(file, editor);
124 if (!CodeInsightUtilBase.prepareFileForWrite(myParameter.getContainingFile())) return;
126 IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace();
127 final PsiType type = getType(myParameter);
128 final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
129 final String parameterName = myParameter.getName();
130 String propertyName = styleManager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER);
132 String fieldNameToCalc;
133 boolean isFinalToCalc;
135 final PsiClass targetClass = PsiTreeUtil.getParentOfType(myParameter, PsiClass.class);
136 final PsiMethod method = (PsiMethod)myParameter.getDeclarationScope();
138 final boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
140 VariableKind kind = isMethodStatic ? VariableKind.STATIC_FIELD : VariableKind.FIELD;
141 SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, type);
142 String[] names = suggestedNameInfo.names;
144 if (isInteractive) {
145 List<String> namesList = new ArrayList<String>();
146 namesList.addAll(Arrays.asList(names));
147 String defaultName = styleManager.propertyNameToVariableName(propertyName, kind);
148 if (namesList.contains(defaultName)) {
149 Collections.swap(namesList, 0, namesList.indexOf(defaultName));
151 else {
152 namesList.add(0, defaultName);
154 names = namesList.toArray(new String[namesList.size()]);
156 boolean myBeFinal = method.isConstructor();
157 CreateFieldFromParameterDialog dialog = new CreateFieldFromParameterDialog(
158 project,
159 names,
160 type.getCanonicalText(), targetClass, myBeFinal);
161 dialog.show();
163 if (!dialog.isOK()) return;
165 fieldNameToCalc = dialog.getEnteredName();
166 isFinalToCalc = dialog.isDeclareFinal();
168 suggestedNameInfo.nameChoosen(fieldNameToCalc);
170 else {
171 isFinalToCalc = !isMethodStatic;
172 fieldNameToCalc = names[0];
175 final boolean isFinal = isFinalToCalc;
176 final String fieldName = fieldNameToCalc;
177 ApplicationManager.getApplication().runWriteAction(new Runnable() {
178 public void run() {
179 try {
180 PsiManager psiManager = PsiManager.getInstance(project);
181 PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
183 PsiField field = factory.createField(fieldName, type);
184 PsiModifierList modifierList = field.getModifierList();
185 modifierList.setModifierProperty(PsiModifier.STATIC, isMethodStatic);
186 modifierList.setModifierProperty(PsiModifier.FINAL, isFinal);
188 if (AnnotationUtil.isAnnotated(myParameter, AnnotationUtil.NULLABLE, false)) {
189 modifierList.addAfter(factory.createAnnotationFromText("@" + AnnotationUtil.NULLABLE, field), null);
192 PsiCodeBlock methodBody = method.getBody();
193 if (methodBody == null) return;
194 PsiStatement[] statements = methodBody.getStatements();
196 Ref<Pair<PsiField, Boolean>> anchorRef = new Ref<Pair<PsiField, Boolean>>();
197 int i = findFieldAssignmentAnchor(statements, anchorRef, targetClass, myParameter);
198 Pair<PsiField, Boolean> fieldAnchor = anchorRef.get();
200 String stmtText = fieldName + " = " + parameterName + ";";
201 if (fieldName.equals(parameterName)) {
202 @NonNls String prefix = isMethodStatic ? targetClass.getName() == null ? "" : targetClass.getName() + "." : "this.";
203 stmtText = prefix + stmtText;
206 PsiStatement assignmentStmt = factory.createStatementFromText(stmtText, methodBody);
207 assignmentStmt = (PsiStatement)CodeStyleManager.getInstance(project).reformat(assignmentStmt);
209 if (i == statements.length) {
210 methodBody.add(assignmentStmt);
212 else {
213 methodBody.addAfter(assignmentStmt, i > 0 ? statements[i - 1] : null);
216 if (fieldAnchor != null) {
217 PsiVariable psiVariable = fieldAnchor.getFirst();
218 psiVariable.normalizeDeclaration();
221 boolean found = false;
222 final PsiField[] fields = targetClass.getFields();
223 for (PsiField f : fields) {
224 if (f.getName().equals(field.getName())) {
225 found = true;
226 break;
230 if (!found) {
231 if (fieldAnchor != null) {
232 Boolean insertBefore = fieldAnchor.getSecond();
233 PsiField inField = fieldAnchor.getFirst();
234 if (insertBefore.booleanValue()) {
235 targetClass.addBefore(field, inField);
237 else {
238 targetClass.addAfter(field, inField);
241 else {
242 targetClass.add(field);
246 catch (IncorrectOperationException e) {
247 LOG.error(e);
253 static int findFieldAssignmentAnchor(final PsiStatement[] statements, @Nullable final Ref<Pair<PsiField, Boolean>> anchorRef,
254 final PsiClass targetClass, final PsiParameter myParameter) {
255 int i = 0;
256 for (; i < statements.length; i++) {
257 PsiStatement psiStatement = statements[i];
259 if (psiStatement instanceof PsiExpressionStatement) {
260 PsiExpressionStatement expressionStatement = (PsiExpressionStatement)psiStatement;
261 PsiExpression expression = expressionStatement.getExpression();
263 if (expression instanceof PsiMethodCallExpression) {
264 PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
265 @NonNls String text = methodCallExpression.getMethodExpression().getText();
267 if (text.equals("super") || text.equals("this")) {
268 continue;
271 else if (expression instanceof PsiAssignmentExpression) {
272 PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression;
273 PsiExpression lExpression = assignmentExpression.getLExpression();
274 PsiExpression rExpression = assignmentExpression.getRExpression();
276 if (!(lExpression instanceof PsiReferenceExpression)) break;
277 if (!(rExpression instanceof PsiReferenceExpression)) break;
279 PsiReferenceExpression lReference = (PsiReferenceExpression)lExpression;
280 PsiReferenceExpression rReference = (PsiReferenceExpression)rExpression;
282 PsiElement lElement = lReference.resolve();
283 PsiElement rElement = rReference.resolve();
285 if (!(lElement instanceof PsiField) || ((PsiField)lElement).getContainingClass() != targetClass) break;
286 if (!(rElement instanceof PsiParameter)) break;
288 if (myParameter.getTextRange().getStartOffset() < rElement.getTextRange().getStartOffset()) {
289 if (anchorRef != null) {
290 anchorRef.set(Pair.create((PsiField)lElement, Boolean.TRUE));
292 break;
295 if (anchorRef != null) {
296 anchorRef.set(Pair.create((PsiField)lElement, Boolean.FALSE));
298 continue;
302 break;
304 return i;
307 public boolean startInWriteAction() {
308 return false;