/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.psi.impl.beanProperties; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PropertyUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; /** * @author Dmitry Avdeev */ public abstract class CreateBeanPropertyFix implements LocalQuickFix, IntentionAction { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.beanProperties.CreateBeanPropertyFix"); private static final CreateBeanPropertyFix[] NO_FIXES = new CreateBeanPropertyFix[0]; protected final String myPropertyName; @NotNull protected final PsiClass myPsiClass; @NotNull protected final PsiType myType; public static LocalQuickFix[] createFixes(String propertyName, @NotNull PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { return (LocalQuickFix[])create(propertyName, psiClass, type, createSetter); } public static IntentionAction[] createActions(String propertyName, @NotNull PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { return (IntentionAction[])create(propertyName, psiClass, type, createSetter); } private static Object[] create(final String propertyName, final PsiClass psiClass, PsiType type, final boolean createSetter) { if (psiClass instanceof PsiCompiledElement) return NO_FIXES; if (type == null) { final Project project = psiClass.getProject(); final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); final PsiClass aClass = facade.findClass(JAVA_LANG_STRING, GlobalSearchScope.allScope(project)); if (aClass == null) { return NO_FIXES; } type = facade.getElementFactory().createType(aClass); } if (psiClass.isInterface()) { return new CreateBeanPropertyFix[] { new CreateAccessorFix(propertyName, psiClass, type, createSetter) }; } return new CreateBeanPropertyFix[] { new CreateBeanPropertyFix(propertyName, psiClass, type) { @Override @NotNull public String getName() { return QuickFixBundle.message("create.readable.writable.property.with.field", myPropertyName); } @Override protected void doFix() throws IncorrectOperationException { createField(); createSetter(true); createGetter(true); } }, new CreateAccessorFix(propertyName, psiClass, type, createSetter), new CreateBeanPropertyFix(propertyName, psiClass, type) { @Override protected void doFix() throws IncorrectOperationException { createField(); if (createSetter) { createSetter(true); } else { createGetter(true); } } @Override @NotNull public String getName() { return QuickFixBundle.message(createSetter ? "create.writable.property.with.field" : "create.readable.property.with.field", myPropertyName); } } }; } protected CreateBeanPropertyFix(String propertyName, @NotNull PsiClass psiClass, @NotNull PsiType type) { myPropertyName = propertyName; myPsiClass = psiClass; myType = type; } @Override @NotNull public String getFamilyName() { return getName(); } @Override public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { applyFix(project); } private void applyFix(final Project project) { new WriteCommandAction.Simple(project, getName(), myPsiClass.getContainingFile()) { @Override protected void run() throws Throwable { try { doFix(); } catch (IncorrectOperationException e) { LOG.error("Cannot create property", e); } } }.execute(); } @Override @NotNull public String getText() { return getName(); } @Override public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { return true; } @Override public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { applyFix(project); } @Override public boolean startInWriteAction() { return false; } protected abstract void doFix() throws IncorrectOperationException; private String getFieldName() { final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(myPsiClass.getProject()); return styleManager.suggestVariableName(VariableKind.FIELD, myPropertyName, null, myType).names[0]; } protected PsiElement createSetter(final boolean createField) throws IncorrectOperationException { final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myPsiClass.getProject()).getElementFactory(); final String methodName = PropertyUtil.suggestSetterName(myPropertyName); final String typeName = myType.getCanonicalText(); @NonNls final String text; boolean isInterface = myPsiClass.isInterface(); if (isInterface) { text = "public void " + methodName + "(" + typeName + " " + myPropertyName + ");"; } else if (createField) { @NonNls String fieldName = getFieldName(); if (fieldName.equals(myPropertyName)) { fieldName = "this." + fieldName; } text = "public void " + methodName + "(" + typeName + " " + myPropertyName + ") {" + fieldName + "=" + myPropertyName + ";}"; } else { text = "public void " + methodName + "(" + typeName + " " + myPropertyName + ") {}"; } final PsiMethod method = elementFactory.createMethodFromText(text, null); final PsiMethod psiElement = (PsiMethod)myPsiClass.add(method); if (!isInterface && !createField) { CreateFromUsageUtils.setupMethodBody(psiElement, myPsiClass); } return psiElement; } protected PsiElement createGetter(final boolean createField) throws IncorrectOperationException { final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myPsiClass.getProject()).getElementFactory(); final String methodName = PropertyUtil.suggestGetterName(myPropertyName, myType); final String typeName = myType.getCanonicalText(); @NonNls final String text; boolean isInterface = myPsiClass.isInterface(); if (createField) { final String fieldName = getFieldName(); text = "public " + typeName + " " + methodName + "() { return " + fieldName + "; }"; } else { if (isInterface) { text = typeName + " " + methodName + "();"; } else { text = "public " + typeName + " " + methodName + "() { return null; }"; } } final PsiMethod method = elementFactory.createMethodFromText(text, null); final PsiMethod psiElement = (PsiMethod)myPsiClass.add(method); if (!createField && !isInterface) { CreateFromUsageUtils.setupMethodBody(psiElement); } return psiElement; } protected PsiElement createField() throws IncorrectOperationException { final String fieldName = getFieldName(); final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myPsiClass.getProject()).getElementFactory(); final PsiField psiField = elementFactory.createField(fieldName, myType); return myPsiClass.add(psiField); } private static class CreateAccessorFix extends CreateBeanPropertyFix { private final boolean myCreateSetter; public CreateAccessorFix(String propertyName, PsiClass psiClass, PsiType type, boolean createSetter) { super(propertyName, psiClass, type); myCreateSetter = createSetter; } @Override protected void doFix() throws IncorrectOperationException { if (myCreateSetter) { createSetter(false); } else { createGetter(false); } } @Override @NotNull public String getName() { return QuickFixBundle.message(myCreateSetter ? "create.writable.property" : "create.readable.property", myPropertyName); } } }