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
.daemon
.impl
.quickfix
;
18 import com
.intellij
.codeInsight
.CodeInsightUtilBase
;
19 import com
.intellij
.codeInsight
.daemon
.QuickFixBundle
;
20 import com
.intellij
.codeInsight
.intention
.impl
.TypeExpression
;
21 import com
.intellij
.codeInsight
.lookup
.LookupElement
;
22 import com
.intellij
.codeInsight
.lookup
.LookupItemUtil
;
23 import com
.intellij
.codeInsight
.template
.*;
24 import com
.intellij
.codeInsight
.template
.impl
.TemplateState
;
25 import com
.intellij
.openapi
.application
.ApplicationManager
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.editor
.Editor
;
28 import com
.intellij
.openapi
.fileEditor
.ex
.IdeDocumentHistory
;
29 import com
.intellij
.openapi
.project
.Project
;
30 import com
.intellij
.openapi
.util
.TextRange
;
31 import com
.intellij
.psi
.*;
32 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
33 import com
.intellij
.psi
.codeStyle
.VariableKind
;
34 import com
.intellij
.psi
.util
.PropertyUtil
;
35 import com
.intellij
.psi
.util
.PsiTreeUtil
;
36 import com
.intellij
.psi
.util
.PsiUtil
;
37 import com
.intellij
.util
.IncorrectOperationException
;
38 import org
.jetbrains
.annotations
.NonNls
;
39 import org
.jetbrains
.annotations
.NotNull
;
41 import java
.util
.ArrayList
;
42 import java
.util
.LinkedHashSet
;
43 import java
.util
.List
;
49 public class CreatePropertyFromUsageFix
extends CreateFromUsageBaseFix
{
50 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.CreatePropertyFromUsageFix");
51 @NonNls private static final String FIELD_VARIABLE
= "FIELD_NAME_VARIABLE";
52 @NonNls private static final String TYPE_VARIABLE
= "FIELD_TYPE_VARIABLE";
53 @NonNls private static final String GET_PREFIX
= "get";
54 @NonNls private static final String IS_PREFIX
= "is";
55 @NonNls private static final String SET_PREFIX
= "set";
57 public CreatePropertyFromUsageFix(PsiMethodCallExpression methodCall
) {
58 myMethodCall
= methodCall
;
61 private final PsiMethodCallExpression myMethodCall
;
64 public String
getFamilyName() {
65 return QuickFixBundle
.message("create.property.from.usage.family");
68 protected PsiElement
getElement() {
69 if (!myMethodCall
.isValid() || !myMethodCall
.getManager().isInProject(myMethodCall
)) return null;
73 protected boolean isAvailableImpl(int offset
) {
74 if (CreateMethodFromUsageFix
.hasErrorsInArgumentList(myMethodCall
)) return false;
75 PsiReferenceExpression ref
= myMethodCall
.getMethodExpression();
76 String methodName
= myMethodCall
.getMethodExpression().getReferenceName();
77 LOG
.assertTrue(methodName
!= null);
78 String propertyName
= PropertyUtil
.getPropertyName(methodName
);
79 if (propertyName
== null || propertyName
.length() == 0) return false;
81 String getterOrSetter
= null;
82 if (methodName
.startsWith(GET_PREFIX
) || methodName
.startsWith(IS_PREFIX
)) {
83 if (myMethodCall
.getArgumentList().getExpressions().length
!= 0) return false;
84 getterOrSetter
= QuickFixBundle
.message("create.getter");
86 else if (methodName
.startsWith(SET_PREFIX
)) {
87 if (myMethodCall
.getArgumentList().getExpressions().length
!= 1) return false;
88 getterOrSetter
= QuickFixBundle
.message("create.setter");
91 LOG
.error("Internal error in create property intention");
94 List
<PsiClass
> classes
= getTargetClasses(myMethodCall
);
95 if (classes
.isEmpty()) return false;
97 for (PsiClass aClass
: classes
) {
98 if (!aClass
.isInterface()) {
99 if (CreateFromUsageUtils
.shouldShowTag(offset
, ref
.getReferenceNameElement(), myMethodCall
)) {
100 setText(getterOrSetter
);
112 static class FieldExpression
extends Expression
{
113 private final String myDefaultFieldName
;
114 private final PsiField myField
;
115 private final PsiClass myClass
;
116 private final PsiType
[] myExpectedTypes
;
118 public FieldExpression(PsiField field
, PsiClass aClass
, PsiType
[] expectedTypes
) {
121 myExpectedTypes
= expectedTypes
;
122 myDefaultFieldName
= field
.getName();
125 public Result
calculateResult(ExpressionContext context
) {
126 return new TextResult(myDefaultFieldName
);
129 public Result
calculateQuickResult(ExpressionContext context
) {
130 return new TextResult(myDefaultFieldName
);
133 public LookupElement
[] calculateLookupItems(ExpressionContext context
) {
134 Set
<LookupElement
> set
= new LinkedHashSet
<LookupElement
>();
135 set
.add(LookupItemUtil
.objectToLookupItem(myField
));
136 PsiField
[] fields
= myClass
.getFields();
137 for (PsiField otherField
: fields
) {
138 if (!myDefaultFieldName
.equals(otherField
.getName())) {
139 PsiType otherType
= otherField
.getType();
140 for (PsiType type
: myExpectedTypes
) {
141 if (type
.equals(otherType
)) {
142 set
.add(LookupItemUtil
.objectToLookupItem(otherField
));
148 if (set
.size() < 2) return null;
149 return set
.toArray(new LookupElement
[set
.size()]);
154 protected List
<PsiClass
> getTargetClasses(PsiElement element
) {
155 List
<PsiClass
> all
= super.getTargetClasses(element
);
156 if (all
.isEmpty()) return all
;
158 List
<PsiClass
> nonInterfaces
= new ArrayList
<PsiClass
>();
159 for (PsiClass aClass
: all
) {
160 if (!aClass
.isInterface()) nonInterfaces
.add(aClass
);
162 return nonInterfaces
;
165 protected void invokeImpl(PsiClass targetClass
) {
166 PsiManager manager
= myMethodCall
.getManager();
167 final Project project
= manager
.getProject();
168 PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
170 boolean isStatic
= false;
171 PsiExpression qualifierExpression
= myMethodCall
.getMethodExpression().getQualifierExpression();
172 if (qualifierExpression
!= null) {
173 PsiReference reference
= qualifierExpression
.getReference();
174 if (reference
!= null) {
175 isStatic
= reference
.resolve() instanceof PsiClass
;
179 PsiMethod method
= PsiTreeUtil
.getParentOfType(myMethodCall
, PsiMethod
.class);
180 if (method
!= null) {
181 isStatic
= method
.hasModifierProperty(PsiModifier
.STATIC
);
184 String fieldName
= getVariableName(myMethodCall
, isStatic
);
185 LOG
.assertTrue(fieldName
!= null);
186 String callText
= myMethodCall
.getMethodExpression().getReferenceName();
187 LOG
.assertTrue(callText
!= null, myMethodCall
.getMethodExpression());
188 PsiType
[] expectedTypes
;
190 if (callText
.startsWith(GET_PREFIX
)) {
191 expectedTypes
= CreateFromUsageUtils
.guessType(myMethodCall
, false);
192 type
= expectedTypes
[0];
194 else if (callText
.startsWith(IS_PREFIX
)) {
195 type
= PsiType
.BOOLEAN
;
196 expectedTypes
= new PsiType
[]{type
};
199 type
= myMethodCall
.getArgumentList().getExpressions()[0].getType();
200 if (type
== null || PsiType
.NULL
.equals(type
)) type
= PsiType
.getJavaLangObject(manager
, myMethodCall
.getResolveScope());
201 expectedTypes
= new PsiType
[]{type
};
204 positionCursor(project
, targetClass
.getContainingFile(), targetClass
);
206 IdeDocumentHistory
.getInstance(project
).includeCurrentPlaceAsChangePlace();
209 PsiField field
= targetClass
.findFieldByName(fieldName
, true);
211 field
= factory
.createField(fieldName
, type
);
212 PsiUtil
.setModifierProperty(field
, PsiModifier
.STATIC
, isStatic
);
215 PsiElement fieldReference
;
216 PsiElement typeReference
;
218 if (callText
.startsWith(GET_PREFIX
) || callText
.startsWith(IS_PREFIX
)) {
219 accessor
= (PsiMethod
)targetClass
.add(PropertyUtil
.generateGetterPrototype(field
));
220 body
= accessor
.getBody();
221 LOG
.assertTrue(body
!= null, accessor
.getText());
222 fieldReference
= ((PsiReturnStatement
)body
.getStatements()[0]).getReturnValue();
223 typeReference
= accessor
.getReturnTypeElement();
226 accessor
= (PsiMethod
)targetClass
.add(PropertyUtil
.generateSetterPrototype(field
, targetClass
));
227 body
= accessor
.getBody();
228 LOG
.assertTrue(body
!= null, accessor
.getText());
229 PsiAssignmentExpression expr
= (PsiAssignmentExpression
)((PsiExpressionStatement
)body
.getStatements()[0]).getExpression();
230 fieldReference
= ((PsiReferenceExpression
)expr
.getLExpression()).getReferenceNameElement();
231 typeReference
= accessor
.getParameterList().getParameters()[0].getTypeElement();
233 accessor
.setName(callText
);
234 PsiUtil
.setModifierProperty(accessor
, PsiModifier
.STATIC
, isStatic
);
236 TemplateBuilderImpl builder
= new TemplateBuilderImpl(accessor
);
237 builder
.replaceElement(typeReference
, TYPE_VARIABLE
, new TypeExpression(project
, expectedTypes
), true);
238 builder
.replaceElement(fieldReference
, FIELD_VARIABLE
, new FieldExpression(field
, targetClass
, expectedTypes
), true);
239 builder
.setEndVariableAfter(body
.getLBrace());
241 accessor
= CodeInsightUtilBase
.forcePsiPostprocessAndRestoreElement(accessor
);
242 targetClass
= accessor
.getContainingClass();
243 LOG
.assertTrue(targetClass
!= null);
244 Template template
= builder
.buildTemplate();
245 TextRange textRange
= accessor
.getTextRange();
246 final PsiFile file
= targetClass
.getContainingFile();
247 final Editor editor
= positionCursor(project
, targetClass
.getContainingFile(), accessor
);
248 editor
.getDocument().deleteString(textRange
.getStartOffset(), textRange
.getEndOffset());
249 editor
.getCaretModel().moveToOffset(textRange
.getStartOffset());
251 final boolean isStatic1
= isStatic
;
252 startTemplate(editor
, template
, project
, new TemplateEditingAdapter() {
253 public void beforeTemplateFinished(final TemplateState state
, Template template
) {
254 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
256 String fieldName
= state
.getVariableValue(FIELD_VARIABLE
).getText();
257 if (!JavaPsiFacade
.getInstance(project
).getNameHelper().isIdentifier(fieldName
)) return;
258 String fieldType
= state
.getVariableValue(TYPE_VARIABLE
).getText();
260 PsiElement element
= file
.findElementAt(editor
.getCaretModel().getOffset());
261 PsiClass aClass
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
262 if (aClass
== null) return;
263 if (aClass
.findFieldByName(fieldName
, true) != null) return;
264 PsiElementFactory factory
= JavaPsiFacade
.getInstance(aClass
.getProject()).getElementFactory();
266 PsiType type
= factory
.createTypeFromText(fieldType
, aClass
);
268 PsiField field
= factory
.createField(fieldName
, type
);
269 field
= (PsiField
)aClass
.add(field
);
270 PsiUtil
.setModifierProperty(field
, PsiModifier
.STATIC
, isStatic1
);
271 positionCursor(project
, field
.getContainingFile(), field
);
273 catch (IncorrectOperationException e
) {
277 catch (IncorrectOperationException e
) {
284 catch (IncorrectOperationException e
) {
289 private static String
getVariableName(PsiMethodCallExpression methodCall
, boolean isStatic
) {
290 JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(methodCall
.getProject());
291 String methodName
= methodCall
.getMethodExpression().getReferenceName();
292 String propertyName
= PropertyUtil
.getPropertyName(methodName
);
293 if (propertyName
!= null && propertyName
.length() > 0) {
294 VariableKind kind
= isStatic ? VariableKind
.STATIC_FIELD
: VariableKind
.FIELD
;
295 return codeStyleManager
.propertyNameToVariableName(propertyName
, kind
);
301 protected boolean isValidElement(PsiElement element
) {
302 PsiMethodCallExpression methodCall
= (PsiMethodCallExpression
) element
;
303 return methodCall
.getMethodExpression().resolve() != null;