From cfeb6c005e7a5e4dd3798dc02fe3b3c2586238d6 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 5 Jul 2017 17:23:21 +0300 Subject: [PATCH] `JvmCommonIntentionActionsFactory` switched from UAST to `@JvmCommon`-annotated API --- .../JvmCommonIntentionActionsFactory.kt | 79 +++++++++++++------ .../inheritance/ImplicitSubclassInspection.kt | 5 +- .../impl/JavaCommonIntentionActionsFactory.kt | 60 ++++++++------ .../CreateBeanPropertyFixes.java | 13 ++- .../src/com/intellij/psi/JvmCommon.java | 41 ++++++++++ 5 files changed, 142 insertions(+), 56 deletions(-) create mode 100644 java/java-psi-api/src/com/intellij/psi/JvmCommon.java 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 675cef723f0e..e8927de8a0f7 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 @@ -17,13 +17,13 @@ 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 -import com.intellij.psi.PsiTypeParameter +import com.intellij.openapi.components.ServiceManager +import com.intellij.psi.* import org.jetbrains.annotations.ApiStatus import org.jetbrains.uast.UClass import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UParameter +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastContext /** * Extension Point provides language-abstracted code modifications for JVM-based languages. @@ -38,18 +38,22 @@ import org.jetbrains.uast.UParameter @ApiStatus.Experimental abstract class JvmCommonIntentionActionsFactory { - open fun createChangeModifierAction(declaration: UDeclaration, + open fun createChangeModifierAction(declaration: @JvmCommon PsiModifierListOwner, @PsiModifier.ModifierConstant modifier: String, - shouldPresent: Boolean): IntentionAction? = null + shouldPresent: Boolean): IntentionAction? = + //Fallback if Uast-version of method is overridden + createChangeModifierAction(declaration.asUast(), modifier, shouldPresent) open fun createAddCallableMemberActions(info: MethodInsertionInfo): List = emptyList() - open fun createAddBeanPropertyActions(uClass: UClass, + open fun createAddBeanPropertyActions(psiClass: @JvmCommon PsiClass, propertyName: String, @PsiModifier.ModifierConstant visibilityModifier: String, propertyType: PsiType, setterRequired: Boolean, - getterRequired: Boolean): List = emptyList() + getterRequired: Boolean): List = + //Fallback if Uast-version of method is overridden + createAddBeanPropertyActions(psiClass.asUast(), propertyName, visibilityModifier, propertyType, setterRequired, getterRequired) companion object : LanguageExtension( "com.intellij.codeInsight.intention.jvmCommonIntentionActionsFactory") { @@ -58,49 +62,78 @@ abstract class JvmCommonIntentionActionsFactory { override fun forLanguage(l: Language): JvmCommonIntentionActionsFactory? = super.forLanguage(l) } + //A fallback to old api + @Deprecated("use or/and override @JvmCommon-version of this method instead") + open fun createChangeModifierAction(declaration: UDeclaration, + @PsiModifier.ModifierConstant modifier: String, + shouldPresent: Boolean): IntentionAction? = null + + @Deprecated("use or/and override @JvmCommon-version of this method instead") + open fun createAddBeanPropertyActions(uClass: UClass, + propertyName: String, + @PsiModifier.ModifierConstant visibilityModifier: String, + propertyType: PsiType, + setterRequired: Boolean, + getterRequired: Boolean): List = emptyList() + + } +@ApiStatus.Experimental sealed class MethodInsertionInfo( - val containingClass: UClass, + val targetClass: @JvmCommon PsiClass, @PsiModifier.ModifierConstant val modifiers: List = emptyList(), val typeParams: List = emptyList(), - val parameters: List = emptyList() + val parameters: List<@JvmCommon PsiParameter> = emptyList() ) { + + @Deprecated("use `targetClass`", ReplaceWith("targetClass")) + val containingClass: UClass + get() = targetClass.asUast() + companion object { @JvmStatic - fun constructorInfo(containingClass: UClass, parameters: List) = - Constructor(containingClass = containingClass, parameters = parameters) + fun constructorInfo(targetClass: @JvmCommon PsiClass, parameters: List<@JvmCommon PsiParameter>) = + Constructor(targetClass = targetClass, parameters = parameters) @JvmStatic - fun simpleMethodInfo(containingClass: UClass, + fun simpleMethodInfo(containingClass: @JvmCommon PsiClass, methodName: String, @PsiModifier.ModifierConstant modifier: String, returnType: PsiType, - parameters: List) = + parameters: List<@JvmCommon PsiParameter>) = Method(name = methodName, modifiers = listOf(modifier), - containingClass = containingClass, + targetClass = containingClass, returnType = returnType, parameters = parameters) + } class Method( - containingClass: UClass, + targetClass: @JvmCommon PsiClass, val name: String, modifiers: List = emptyList(), - typeParams: List = emptyList(), + typeParams: List<@JvmCommon PsiTypeParameter> = emptyList(), val returnType: PsiType, - parameters: List = emptyList(), + parameters: List<@JvmCommon PsiParameter> = emptyList(), val isAbstract: Boolean = false - ) : MethodInsertionInfo(containingClass, modifiers, typeParams, parameters) + ) : MethodInsertionInfo(targetClass, modifiers, typeParams, parameters) class Constructor( - containingClass: UClass, + targetClass: @JvmCommon PsiClass, modifiers: List = emptyList(), - typeParams: List = emptyList(), - parameters: List = emptyList() - ) : MethodInsertionInfo(containingClass, modifiers, typeParams, parameters) + typeParams: List<@JvmCommon PsiTypeParameter> = emptyList(), + parameters: List<@JvmCommon PsiParameter> = emptyList() + ) : MethodInsertionInfo(targetClass, modifiers, typeParams, parameters) +} + +@Deprecated("remove after kotlin plugin will be ported") +private inline fun PsiElement.asUast(): T = when (this) { + is T -> this + else -> this.let { ServiceManager.getService(project, UastContext::class.java).convertElement(this, null, T::class.java) as T? } + ?: throw UnsupportedOperationException("cant convert $this to ${T::class}") } \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt index 54bfa0647d3c..7f86add69759 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt @@ -17,8 +17,8 @@ package com.intellij.codeInspection.inheritance import com.intellij.CommonBundle import com.intellij.codeInsight.daemon.QuickFixBundle -import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInspection.* import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger @@ -185,7 +185,8 @@ class ImplicitSubclassInspection : AbstractBaseUastLocalInspectionTool() { shouldPresent: Boolean, actionsList: SmartList) { if (declaration.modifierList?.hasModifierProperty(name) != shouldPresent) { - (actionsFactory.createChangeModifierAction(declaration, name, shouldPresent))?.let { + (actionsFactory.createChangeModifierAction(declaration as @com.intellij.psi.JvmCommon PsiModifierListOwner, name, + shouldPresent))?.let { actionsList.add(it) } } 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 abeab071f942..e27970f05a7d 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 @@ -22,46 +22,47 @@ import com.intellij.codeInsight.intention.AbstractIntentionAction import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInsight.intention.MethodInsertionInfo +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.application.runWriteAction 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.impl.beanProperties.CreateJavaBeanPropertyFix import com.intellij.psi.util.PsiFormatUtil import com.intellij.psi.util.PsiFormatUtilBase import com.intellij.util.VisibilityUtil import org.jetbrains.annotations.NotNull -import org.jetbrains.uast.UClass -import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UParameter - class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { - override fun createChangeModifierAction(declaration: UDeclaration, modifier: String, shouldPresent: Boolean): IntentionAction { + override fun createChangeModifierAction(declaration: @JvmCommon PsiModifierListOwner, + modifier: String, + shouldPresent: Boolean): IntentionAction { return ModifierFix(declaration.modifierList, modifier, shouldPresent, false) } override fun createAddCallableMemberActions(info: MethodInsertionInfo): List { return when (info) { is MethodInsertionInfo.Method -> with(info) { - createAddMethodAction(containingClass, name, modifiers.joinToString(" "), returnType, parameters) + createAddMethodAction(targetClass, name, modifiers.joinToString(" "), returnType, parameters) ?.let { listOf(it) } ?: emptyList() } is MethodInsertionInfo.Constructor -> - listOf(AddConstructorFix(info.containingClass.psi, info.parameters.map { it.psi })) + listOf(AddConstructorFix(info.targetClass.javaPsi(), info.parameters.map { it.javaPsi() })) } } - private fun createAddMethodAction(uClass: UClass, + private fun createAddMethodAction(psiClass: @JvmCommon PsiClass, methodName: String, @PsiModifier.ModifierConstant @NotNull visibilityModifier: String, returnType: PsiType, - parameters: List): IntentionAction? { - val paramsString = parameters.mapIndexed { i, t -> "${t.type.presentableText} ${t.name ?: "arg$i"}" }.joinToString() + parameters: List<@JvmCommon PsiParameter>): IntentionAction? { + val paramsString = parameters.mapIndexed { i, t -> "${t.type.canonicalText} ${t.name ?: "arg$i"}" }.joinToString() val signatureString = - "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.presentableText} $methodName($paramsString){}" - val targetClassPointer = SmartPointerManager.getInstance(uClass.project).createSmartPsiElementPointer(uClass.psi) + "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.canonicalText} $methodName($paramsString){}" + val targetClassPointer = SmartPointerManager.getInstance(psiClass.project).createSmartPsiElementPointer(psiClass.javaPsi()) return object : AbstractIntentionAction() { private val text = targetClassPointer.element?.let { psiClass -> @@ -77,17 +78,30 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { override fun getText(): String = text override fun invoke(project: Project, editor: Editor?, file: PsiFile) { - targetClassPointer.element?.let { targetClass -> - targetClass.add(createMethod(targetClass)) + val targetClass = targetClassPointer.element ?: return + runWriteAction { + val addedMethod = targetClass.add(createMethod(targetClass)) + JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedMethod) } } - private fun createMethod(targetClass: PsiClass): PsiMethod = PsiElementFactory.SERVICE.getInstance(targetClass.project) - .createMethodFromText(signatureString, targetClass) + private fun createMethod(targetClass: PsiClass): PsiMethod { + val elementFactory = JVMElementFactories.getFactory(targetClass.language, targetClass.project) // it could be Groovy + ?: JavaPsiFacade.getElementFactory(targetClass.project) + return elementFactory.createMethodFromText(signatureString, targetClass) + } } } - override fun createAddBeanPropertyActions(uClass: UClass, + private inline fun T.javaPsi(): T { + assert(language == JavaLanguage.INSTANCE) + return when (this) { + is org.jetbrains.uast.UElement -> psi as T + else -> this + } + } + + override fun createAddBeanPropertyActions(psiClass: @JvmCommon PsiClass, propertyName: String, @PsiModifier.ModifierConstant visibilityModifier: String, propertyType: PsiType, @@ -95,20 +109,20 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { getterRequired: Boolean): List { if (getterRequired && setterRequired) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, false)) if (getterRequired || setterRequired) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, false), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, true, true, true)) + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, true, true, true)) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, true)) + CreateJavaBeanPropertyFix(psiClass.javaPsi(), 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 index b300bb34803e..1f1cd47a37ca 100644 --- a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java @@ -25,8 +25,6 @@ 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 static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; import static com.intellij.util.ArrayUtil.toObjectArray; @@ -35,14 +33,14 @@ import static com.intellij.util.ArrayUtil.toObjectArray; public class CreateBeanPropertyFixes { public static LocalQuickFix[] createFixes(String propertyName, - @NotNull PsiClass psiClass, + @NotNull @JvmCommon PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { return IntentionWrapper.wrapToQuickFixes(createActions(propertyName, psiClass, type, createSetter), psiClass.getContainingFile()); } public static IntentionAction[] createActions(String propertyName, - @NotNull PsiClass psiClass, + @NotNull @JvmCommon PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { if (psiClass instanceof PsiCompiledElement) return IntentionAction.EMPTY_ARRAY; @@ -55,9 +53,8 @@ public class CreateBeanPropertyFixes { } 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 toObjectArray(factory.createAddBeanPropertyActions(uClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter), - IntentionAction.class); + return toObjectArray( + factory.createAddBeanPropertyActions(psiClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter), + IntentionAction.class); } } diff --git a/java/java-psi-api/src/com/intellij/psi/JvmCommon.java b/java/java-psi-api/src/com/intellij/psi/JvmCommon.java new file mode 100644 index 000000000000..a643491097f4 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/JvmCommon.java @@ -0,0 +1,41 @@ +/* + * 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. + * 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; + +import org.jetbrains.annotations.ApiStatus; + +import java.lang.annotation.*; + +/** + * Annotation for marking {@link PsiElement} subclasses usages that they will be used in + * language-abstracted way: + *
    + *
  • No modification methods should be directly called on such instances
  • + *
  • Usages should be aware of {@link PsiElement#getLanguage()}, and not be hardcoded to dedicated language
  • + *
  • Instances could be "virtual" and don't have physical representations
  • + *
  • Complex operations should be performed via corresponding {@link com.intellij.lang.LanguageExtensionPoint} or similar API
  • + *
  • {@link org.jetbrains.uast.UClass}-like instances should be supported
  • + *
+ * Note: this annotation considered as kind-of "transitional" until generic language-independent API will be implemented + */ +@ApiStatus.Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ + ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER +}) +public @interface JvmCommon { +}