Spring Support: Fixed rename of custom-named beans specified with Kotlin annotation
#KT-12096 Fixed
This commit is contained in:
@@ -89,6 +89,7 @@
|
|||||||
###### Issues fixed
|
###### Issues fixed
|
||||||
|
|
||||||
- [`KT-12091`](https://youtrack.jetbrains.com/issue/KT-12091) Fixed unstable behavior of Spring line markers
|
- [`KT-12091`](https://youtrack.jetbrains.com/issue/KT-12091) Fixed unstable behavior of Spring line markers
|
||||||
|
- [`KT-12096`](https://youtrack.jetbrains.com/issue/KT-12096) Fixed rename of custom-named beans specified with Kotlin annotation
|
||||||
- [`KT-12117`](https://youtrack.jetbrains.com/issue/KT-12117) Group Kotlin classes from the same file in the Choose Bean dialog
|
- [`KT-12117`](https://youtrack.jetbrains.com/issue/KT-12117) Group Kotlin classes from the same file in the Choose Bean dialog
|
||||||
- [`KT-12120`](https://youtrack.jetbrains.com/issue/KT-12120) Show autowiring candidates line markers for @Autowired-annotated constructors and constructor parameters
|
- [`KT-12120`](https://youtrack.jetbrains.com/issue/KT-12120) Show autowiring candidates line markers for @Autowired-annotated constructors and constructor parameters
|
||||||
- [`KT-12122`](https://youtrack.jetbrains.com/issue/KT-12122) Fixed line marker popup on functions with @Qualifier-annotated parameters
|
- [`KT-12122`](https://youtrack.jetbrains.com/issue/KT-12122) Fixed line marker popup on functions with @Qualifier-annotated parameters
|
||||||
|
|||||||
@@ -17,13 +17,11 @@
|
|||||||
package org.jetbrains.kotlin.asJava
|
package org.jetbrains.kotlin.asJava
|
||||||
|
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
|
||||||
import org.jetbrains.kotlin.psi.ValueArgument
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||||
@@ -36,9 +34,12 @@ class KtLightAnnotation(
|
|||||||
override val kotlinOrigin: KtAnnotationEntry,
|
override val kotlinOrigin: KtAnnotationEntry,
|
||||||
private val owner: PsiAnnotationOwner
|
private val owner: PsiAnnotationOwner
|
||||||
) : PsiAnnotation by clsDelegate, KtLightElement<KtAnnotationEntry, PsiAnnotation> {
|
) : PsiAnnotation by clsDelegate, KtLightElement<KtAnnotationEntry, PsiAnnotation> {
|
||||||
open inner class LightExpressionValue<out D : PsiExpression>(val delegate: D) : PsiAnnotationMemberValue, PsiExpression by delegate {
|
open inner class LightExpressionValue<out D : PsiExpression>(
|
||||||
|
val delegate: D,
|
||||||
|
private val parent: PsiElement
|
||||||
|
) : PsiAnnotationMemberValue, PsiExpression by delegate {
|
||||||
val originalExpression: PsiElement? by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
val originalExpression: PsiElement? by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
val nameAndValue = getStrictParentOfType<PsiNameValuePair>() ?: return@lazy null
|
val nameAndValue = delegate.getStrictParentOfType<PsiNameValuePair>() ?: return@lazy null
|
||||||
val annotationEntry = this@KtLightAnnotation.kotlinOrigin
|
val annotationEntry = this@KtLightAnnotation.kotlinOrigin
|
||||||
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
|
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
|
||||||
val resolvedCall = annotationEntry.getResolvedCall(context) ?: return@lazy null
|
val resolvedCall = annotationEntry.getResolvedCall(context) ?: return@lazy null
|
||||||
@@ -81,32 +82,56 @@ class KtLightAnnotation(
|
|||||||
override fun getLanguage() = KotlinLanguage.INSTANCE
|
override fun getLanguage() = KotlinLanguage.INSTANCE
|
||||||
override fun getNavigationElement() = originalExpression
|
override fun getNavigationElement() = originalExpression
|
||||||
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
|
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
|
||||||
|
override fun getParent() = parent
|
||||||
|
|
||||||
|
override fun replace(newElement: PsiElement): PsiElement {
|
||||||
|
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
|
||||||
|
val origin = originalExpression ?: return this
|
||||||
|
|
||||||
|
val exprToReplace =
|
||||||
|
if (origin is KtCallExpression /*arrayOf*/) {
|
||||||
|
unwrapArray(origin.valueArguments)
|
||||||
|
} else {
|
||||||
|
origin as? KtExpression
|
||||||
|
} ?: return this
|
||||||
|
exprToReplace.replace(KtPsiFactory(this).createExpression("\"${StringUtil.escapeStringCharacters(value)}\""))
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class LightStringLiteral(delegate: PsiLiteralExpression): LightExpressionValue<PsiLiteralExpression>(delegate), PsiLiteralExpression {
|
inner class LightStringLiteral(
|
||||||
|
delegate: PsiLiteralExpression,
|
||||||
|
parent: PsiElement
|
||||||
|
): LightExpressionValue<PsiLiteralExpression>(delegate, parent), PsiLiteralExpression {
|
||||||
override fun getValue() = delegate.value
|
override fun getValue() = delegate.value
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class LightClassLiteral(
|
inner class LightClassLiteral(
|
||||||
delegate: PsiClassObjectAccessExpression
|
delegate: PsiClassObjectAccessExpression,
|
||||||
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate), PsiClassObjectAccessExpression {
|
parent: PsiElement
|
||||||
|
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate, parent), PsiClassObjectAccessExpression {
|
||||||
override fun getType() = delegate.type
|
override fun getType() = delegate.type
|
||||||
override fun getOperand(): PsiTypeElement = delegate.operand
|
override fun getOperand(): PsiTypeElement = delegate.operand
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class LightArrayInitializerValue(private val delegate: PsiArrayInitializerMemberValue) : PsiArrayInitializerMemberValue by delegate {
|
inner class LightArrayInitializerValue(
|
||||||
private val _initializers by lazy(LazyThreadSafetyMode.PUBLICATION) { delegate.initializers.map { wrapAnnotationValue(it) }.toTypedArray() }
|
private val delegate: PsiArrayInitializerMemberValue,
|
||||||
|
private val parent: PsiElement
|
||||||
|
) : PsiArrayInitializerMemberValue by delegate {
|
||||||
|
private val _initializers by lazy(LazyThreadSafetyMode.PUBLICATION) { delegate.initializers.map { wrapAnnotationValue(it, this) }.toTypedArray() }
|
||||||
|
|
||||||
override fun getInitializers() = _initializers
|
override fun getInitializers() = _initializers
|
||||||
override fun getLanguage() = KotlinLanguage.INSTANCE
|
override fun getLanguage() = KotlinLanguage.INSTANCE
|
||||||
|
override fun getParent() = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue {
|
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement): PsiAnnotationMemberValue {
|
||||||
return when {
|
return when {
|
||||||
value is PsiLiteralExpression && value.value is String -> LightStringLiteral(value)
|
value is PsiLiteralExpression && value.value is String -> LightStringLiteral(value, parent)
|
||||||
value is PsiClassObjectAccessExpression -> LightClassLiteral(value)
|
value is PsiClassObjectAccessExpression -> LightClassLiteral(value, parent)
|
||||||
value is PsiExpression -> LightExpressionValue(value)
|
value is PsiExpression -> LightExpressionValue(value, parent)
|
||||||
value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value)
|
value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value, parent)
|
||||||
else -> value
|
else -> value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,14 +143,16 @@ class KtLightAnnotation(
|
|||||||
|
|
||||||
override fun getOwner() = owner
|
override fun getOwner() = owner
|
||||||
|
|
||||||
override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it) }
|
override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it, this) }
|
||||||
override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it) }
|
override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it, this) }
|
||||||
|
|
||||||
override fun getText() = kotlinOrigin.text ?: ""
|
override fun getText() = kotlinOrigin.text ?: ""
|
||||||
override fun getTextRange() = kotlinOrigin.textRange ?: TextRange.EMPTY_RANGE
|
override fun getTextRange() = kotlinOrigin.textRange ?: TextRange.EMPTY_RANGE
|
||||||
|
|
||||||
override fun getParent() = owner as? PsiElement
|
override fun getParent() = owner as? PsiElement
|
||||||
|
|
||||||
|
override fun getLanguage() = KotlinLanguage.INSTANCE
|
||||||
|
|
||||||
override fun delete() {
|
override fun delete() {
|
||||||
kotlinOrigin.delete()
|
kotlinOrigin.delete()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -661,6 +661,8 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
implementationClass="org.jetbrains.kotlin.idea.KotlinLightConstantExpressionEvaluator"/>
|
implementationClass="org.jetbrains.kotlin.idea.KotlinLightConstantExpressionEvaluator"/>
|
||||||
|
|
||||||
|
<annotationSupport language="kotlin" implementationClass="com.intellij.psi.impl.source.tree.java.JavaAnnotationSupport"/>
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
|
|||||||
@@ -192,7 +192,9 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
|||||||
|
|
||||||
val substitution = RenamePsiElementProcessor.forElement(toRename).substituteElementToRename(toRename, null)
|
val substitution = RenamePsiElementProcessor.forElement(toRename).substituteElementToRename(toRename, null)
|
||||||
|
|
||||||
runRenameProcessor(context, newName, substitution, true, true)
|
val searchInComments = renameParamsObject["searchInComments"]?.asBoolean ?: true
|
||||||
|
val searchInTextOccurrences = renameParamsObject["searchInTextOccurrences"]?.asBoolean ?: true
|
||||||
|
runRenameProcessor(context, newName, substitution, searchInComments, searchInTextOccurrences)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package bean;
|
||||||
|
|
||||||
|
public class BeanA {
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package bean;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
|
public class JavaRef {
|
||||||
|
@Value("#{ nameJ }") private BeanA valueA;
|
||||||
|
@Value("#{ nameK2 }") private BeanA valueK;
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package bean
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
|
@Configuration open class KotlinConfig {
|
||||||
|
@Bean(name = arrayOf("nameK2")) open fun createBeanK() = BeanA()
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
<bean class="bean.JavaRef" name="refJ">
|
||||||
|
<property name="p1" ref="nameJ"/>
|
||||||
|
<property name="p2" ref="nameK2"/>
|
||||||
|
</bean>
|
||||||
|
</beans>
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"fixtureClasses": ["org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension"],
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "bean/spring-config.xml",
|
||||||
|
"springFileSet": ["bean/spring-config.xml", "bean/KotlinConfig.kt"],
|
||||||
|
"newName": "nameK2",
|
||||||
|
"byRef": "true",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"searchInComments": "false",
|
||||||
|
"searchInTextOccurrences": "false"
|
||||||
|
}
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package bean;
|
||||||
|
|
||||||
|
public class BeanA {
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package bean;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
|
public class JavaRef {
|
||||||
|
@Value("#{ nameJ }") private BeanA valueA;
|
||||||
|
@Value("#{ nameK }") private BeanA valueK;
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package bean
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
|
@Configuration open class KotlinConfig {
|
||||||
|
@Bean(name = arrayOf("nameK")) open fun createBeanK() = BeanA()
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
<bean class="bean.JavaRef" name="refJ">
|
||||||
|
<property name="p1" ref="nameJ"/>
|
||||||
|
<property name="p2" ref="/*rename*/nameK"/>
|
||||||
|
</bean>
|
||||||
|
</beans>
|
||||||
+6
@@ -35,6 +35,12 @@ public class SpringRenameTestGenerated extends AbstractSpringRenameTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("ultimate/testData/spring/core/rename"), Pattern.compile("^(.+)\\.test$"));
|
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("ultimate/testData/spring/core/rename"), Pattern.compile("^(.+)\\.test$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationArgBySpELRefInXMLConf/annotationArgBySpELRefInXMLConf.test")
|
||||||
|
public void testAnnotationArgBySpELRefInXMLConf_AnnotationArgBySpELRefInXMLConf() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/annotationArgBySpELRefInXMLConf/annotationArgBySpELRefInXMLConf.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("classWithXmlRefs/classWithXmlRef.test")
|
@TestMetadata("classWithXmlRefs/classWithXmlRef.test")
|
||||||
public void testClassWithXmlRefs_ClassWithXmlRef() throws Exception {
|
public void testClassWithXmlRefs_ClassWithXmlRef() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/classWithXmlRefs/classWithXmlRef.test");
|
String fileName = KotlinTestUtils.navigationMetadata("ultimate/testData/spring/core/rename/classWithXmlRefs/classWithXmlRef.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user