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
= "";
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();
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);
73 && myParameter
.getDeclarationScope() instanceof PsiMethod
74 && ((PsiMethod
)myParameter
.getDeclarationScope()).getBody() != null
75 && myParameter
.getManager().isInProject(myParameter
)
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;
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
;
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
;
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
));
152 namesList
.add(0, defaultName
);
154 names
= namesList
.toArray(new String
[namesList
.size()]);
156 boolean myBeFinal
= method
.isConstructor();
157 CreateFieldFromParameterDialog dialog
= new CreateFieldFromParameterDialog(
160 type
.getCanonicalText(), targetClass
, myBeFinal
);
163 if (!dialog
.isOK()) return;
165 fieldNameToCalc
= dialog
.getEnteredName();
166 isFinalToCalc
= dialog
.isDeclareFinal();
168 suggestedNameInfo
.nameChoosen(fieldNameToCalc
);
171 isFinalToCalc
= !isMethodStatic
;
172 fieldNameToCalc
= names
[0];
175 final boolean isFinal
= isFinalToCalc
;
176 final String fieldName
= fieldNameToCalc
;
177 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
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
);
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())) {
231 if (fieldAnchor
!= null) {
232 Boolean insertBefore
= fieldAnchor
.getSecond();
233 PsiField inField
= fieldAnchor
.getFirst();
234 if (insertBefore
.booleanValue()) {
235 targetClass
.addBefore(field
, inField
);
238 targetClass
.addAfter(field
, inField
);
242 targetClass
.add(field
);
246 catch (IncorrectOperationException e
) {
253 static int findFieldAssignmentAnchor(final PsiStatement
[] statements
, @Nullable final Ref
<Pair
<PsiField
, Boolean
>> anchorRef
,
254 final PsiClass targetClass
, final PsiParameter myParameter
) {
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")) {
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
));
295 if (anchorRef
!= null) {
296 anchorRef
.set(Pair
.create((PsiField
)lElement
, Boolean
.FALSE
));
307 public boolean startInWriteAction() {