update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / CreatePropertyFromUsageFix.java
blob7315e1dcf7c16c440eeec3845a58c8072bf52e1c
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.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;
44 import java.util.Set;
46 /**
47 * @author ven
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;
63 @NotNull
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;
70 return myMethodCall;
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");
90 else {
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);
101 return true;
103 else {
104 return false;
109 return false;
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) {
119 myField = field;
120 myClass = aClass;
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()]);
153 @NotNull
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;
178 else {
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;
189 PsiType type;
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};
198 else {
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();
208 try {
209 PsiField field = targetClass.findFieldByName(fieldName, true);
210 if (field == null) {
211 field = factory.createField(fieldName, type);
212 PsiUtil.setModifierProperty(field, PsiModifier.STATIC, isStatic);
214 PsiMethod accessor;
215 PsiElement fieldReference;
216 PsiElement typeReference;
217 PsiCodeBlock body;
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();
225 else {
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() {
255 public void run() {
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();
265 try {
266 PsiType type = factory.createTypeFromText(fieldType, aClass);
267 try {
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) {
274 LOG.error(e);
277 catch (IncorrectOperationException e) {
284 catch (IncorrectOperationException e) {
285 LOG.error(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);
298 return null;
301 protected boolean isValidElement(PsiElement element) {
302 PsiMethodCallExpression methodCall = (PsiMethodCallExpression) element;
303 return methodCall.getMethodExpression().resolve() != null;