diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt b/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt index 4719f2b3dfdd..3b74bef789f2 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.intention +import com.intellij.lang.Language import com.intellij.lang.LanguageExtension import com.intellij.psi.PsiModifier import com.intellij.psi.PsiType @@ -36,15 +37,28 @@ import org.jetbrains.uast.UDeclaration @ApiStatus.Experimental abstract class JvmCommonIntentionActionsFactory { - open fun createChangeModifierAction(declaration: UDeclaration, @PsiModifier.ModifierConstant @NonNls modifier: String, shouldPresent: Boolean): IntentionAction? = null + open fun createChangeModifierAction(declaration: UDeclaration, + @PsiModifier.ModifierConstant @NonNls modifier: String, + shouldPresent: Boolean): IntentionAction? = null - open fun createAddMethodAction(u: UClass, + open fun createAddMethodAction(uClass: UClass, methodName: String, @PsiModifier.ModifierConstant visibilityModifier: String, returnType: PsiType, vararg parameters: PsiType): IntentionAction? = null + open fun createAddBeanPropertyActions(uClass: UClass, + propertyName: String, + @PsiModifier.ModifierConstant visibilityModifier: String, + propertyType: PsiType, + setterRequired: Boolean, + getterRequired: Boolean): Array = emptyArray() + companion object : LanguageExtension( - "com.intellij.codeInsight.intention.jvmCommonIntentionActionsFactory") {} + "com.intellij.codeInsight.intention.jvmCommonIntentionActionsFactory") { + + @JvmStatic + override fun forLanguage(l: Language): JvmCommonIntentionActionsFactory? = super.forLanguage(l) + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt b/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt index 04ea271d60ea..92036d1384ae 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt @@ -23,6 +23,7 @@ import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.* +import com.intellij.psi.impl.beanProperties.CreateJavaBeanPropertyFix import com.intellij.util.VisibilityUtil import org.jetbrains.annotations.NotNull import org.jetbrains.uast.UClass @@ -35,7 +36,7 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { return ModifierFix(declaration.modifierList, modifier, shouldPresent, false) } - override fun createAddMethodAction(u: UClass, + override fun createAddMethodAction(uClass: UClass, methodName: String, @PsiModifier.ModifierConstant @NotNull visibilityModifier: String, returnType: PsiType, @@ -43,19 +44,43 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { val paramsString = parameters.mapIndexed { i, t -> "${t.presentableText} arg$i" }.joinToString() val signatureString = "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.presentableText} $methodName($paramsString){}" - val smartPsi = SmartPointerManager.getInstance(u.project).createSmartPsiElementPointer(u.psi) + val smartPsi = SmartPointerManager.getInstance(uClass.project).createSmartPsiElementPointer(uClass.psi) return object : AbstractIntentionAction() { - private val text = QuickFixBundle.message("add.method.text", methodName, u.name) + private val text = QuickFixBundle.message("add.method.text", methodName, uClass.name) override fun getText(): String = text override fun invoke(project: Project, editor: Editor?, file: PsiFile) { val psi = smartPsi.element ?: return - val createMethodFromText = PsiElementFactory.SERVICE.getInstance(u.project) + val createMethodFromText = PsiElementFactory.SERVICE.getInstance(uClass.project) .createMethodFromText(signatureString, psi) psi.add(createMethodFromText) } } } + + override fun createAddBeanPropertyActions(uClass: UClass, + propertyName: String, + @PsiModifier.ModifierConstant visibilityModifier: String, + propertyType: PsiType, + setterRequired: Boolean, + getterRequired: Boolean): Array { + if (getterRequired && setterRequired) + return arrayOf( + CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + true), + CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + false)) + if (getterRequired || setterRequired) + return arrayOf( + CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + true), + CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + false), + CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, true, true, true)) + + return arrayOf( + CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, true)) + } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java new file mode 100644 index 000000000000..bb08a10fe08b --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java @@ -0,0 +1,67 @@ +/* + * 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.intention.IntentionAction; +import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiType; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UClass; +import org.jetbrains.uast.UastContextKt; + +import java.util.Arrays; + +import static com.intellij.codeInspection.IntentionWrapper.wrapToQuickFix; +import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; + +@ApiStatus.Experimental +public class CreateBeanPropertyFixes { + + public static LocalQuickFix[] createFixes(String propertyName, + @NotNull PsiClass psiClass, + @Nullable PsiType type, + final boolean createSetter) { + return Arrays.stream(createActions(propertyName, psiClass, type, createSetter)) + .map(ia -> wrapToQuickFix(ia, psiClass.getContainingFile())) + .toArray(LocalQuickFix[]::new); + } + + public static IntentionAction[] createActions(String propertyName, + @NotNull PsiClass psiClass, + @Nullable PsiType type, + final boolean createSetter) { + 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 IntentionAction.EMPTY_ARRAY; + type = facade.getElementFactory().createType(aClass); + } + JvmCommonIntentionActionsFactory factory = JvmCommonIntentionActionsFactory.forLanguage(psiClass.getLanguage()); + if (factory == null) return IntentionAction.EMPTY_ARRAY; + UClass uClass = UastContextKt.toUElement(psiClass, UClass.class); + if (uClass == null) return IntentionAction.EMPTY_ARRAY; + return factory.createAddBeanPropertyActions(uClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter); + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFix.java b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateJavaBeanPropertyFix.java similarity index 52% rename from java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFix.java rename to java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateJavaBeanPropertyFix.java index a53bd1c9d187..e435264e8b76 100644 --- a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFix.java +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateJavaBeanPropertyFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -27,91 +27,49 @@ 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]; +public class CreateJavaBeanPropertyFix implements LocalQuickFix, IntentionAction { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.beanProperties.CreateJavaBeanPropertyFix"); protected final String myPropertyName; - @NotNull protected final PsiClass myPsiClass; + @NotNull protected final SmartPsiElementPointer myPsiClass; @NotNull protected final PsiType myType; + private final boolean myGetter; + private final boolean mySetter; + private final boolean myField; - 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) { + public CreateJavaBeanPropertyFix(@NotNull PsiClass psiClass, @NotNull String propertyName, + @NotNull PsiType propertyType, + boolean getterRequired, + boolean setterRequired, + boolean fieldRequired) { myPropertyName = propertyName; - myPsiClass = psiClass; - myType = type; + myPsiClass = SmartPointerManager.getInstance(psiClass.getProject()).createSmartPsiElementPointer(psiClass); + myType = propertyType; + myGetter = getterRequired; + mySetter = setterRequired; + myField = fieldRequired; + } + + @Override + @NotNull + public String getName() { + if (myGetter && mySetter && myField) return QuickFixBundle.message("create.readable.writable.property.with.field", myPropertyName); + if (myField && myGetter) return QuickFixBundle.message("create.readable.property.with.field", myPropertyName); + if (myField && mySetter) return QuickFixBundle.message("create.writable.property.with.field", myPropertyName); + if (!myField && myGetter) return QuickFixBundle.message("create.readable.property", myPropertyName); + if (!myField && mySetter) return QuickFixBundle.message("create.writable.property", myPropertyName); + return QuickFixBundle.message("create.readable.writable.property.with.field", myPropertyName); + } + + protected void doFix() throws IncorrectOperationException { + if (myField) createField(); + if (mySetter) createSetter(myField); + if (myGetter) createGetter(myField); } @Override @@ -160,20 +118,20 @@ public abstract class CreateBeanPropertyFix implements LocalQuickFix, IntentionA 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 { + private void 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(); + PsiClass psiClass = myPsiClass.getElement(); + if (psiClass == null) return; + boolean isInterface = psiClass.isInterface(); if (isInterface) { text = "public void " + methodName + "(" + typeName + " " + myPropertyName + ");"; } @@ -188,23 +146,25 @@ public abstract class CreateBeanPropertyFix implements LocalQuickFix, IntentionA text = "public void " + methodName + "(" + typeName + " " + myPropertyName + ") {}"; } final PsiMethod method = elementFactory.createMethodFromText(text, null); - final PsiMethod psiElement = (PsiMethod)myPsiClass.add(method); + final PsiMethod psiElement = (PsiMethod)psiClass.add(method); if (!isInterface && !createField) { - CreateFromUsageUtils.setupMethodBody(psiElement, myPsiClass); + CreateFromUsageUtils.setupMethodBody(psiElement, psiClass); } - return psiElement; } - protected PsiElement createGetter(final boolean createField) throws IncorrectOperationException { + private void 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(); + PsiClass psiClass = myPsiClass.getElement(); + if (psiClass == null) return; + boolean isInterface = psiClass.isInterface(); if (createField) { final String fieldName = getFieldName(); text = "public " + typeName + " " + methodName + "() { return " + fieldName + "; }"; - } else { + } + else { if (isInterface) { text = typeName + " " + methodName + "();"; } @@ -213,42 +173,18 @@ public abstract class CreateBeanPropertyFix implements LocalQuickFix, IntentionA } } final PsiMethod method = elementFactory.createMethodFromText(text, null); - final PsiMethod psiElement = (PsiMethod)myPsiClass.add(method); + final PsiMethod psiElement = (PsiMethod)psiClass.add(method); if (!createField && !isInterface) { CreateFromUsageUtils.setupMethodBody(psiElement); } - return psiElement; } - protected PsiElement createField() throws IncorrectOperationException { + private void 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); - } + PsiClass psiClass = myPsiClass.getElement(); + if (psiClass == null) return; + psiClass.add(psiField); } } diff --git a/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java b/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java index 0beb3e1ae31b..81834e6982be 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java +++ b/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java @@ -26,6 +26,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -102,5 +103,12 @@ public class IntentionWrapper implements LocalQuickFix, IntentionAction, ActionC public IntentionAction getDelegate() { return myAction; } -} + @Contract("null, _ -> null") + public static LocalQuickFix wrapToQuickFix(@Nullable IntentionAction action, @NotNull PsiFile file) { + if (action == null) return null; + if (action instanceof LocalQuickFix) return (LocalQuickFix)action; + return new IntentionWrapper(action, file); + } + +}