172: [Spring] Revert "Fixes for 173 after 28.08.17 changes in IDEA (#1271)"

This reverts commit 3a37d18e5d65043fc99a28772303d948e0f89f6c.
This commit is contained in:
Nikolay Krasko
2018-01-11 20:12:59 +03:00
parent 060ed5db88
commit c6b888678f
4 changed files with 560 additions and 9 deletions
@@ -0,0 +1,512 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.idea.spring.generate
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.template.Expression
import com.intellij.codeInsight.template.ExpressionContext
import com.intellij.codeInsight.template.TemplateBuilderImpl
import com.intellij.codeInsight.template.TextResult
import com.intellij.codeInspection.SmartHashMap
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.util.PropertyUtil
import com.intellij.psi.util.PsiTypesUtil
import com.intellij.spring.CommonSpringModel
import com.intellij.spring.model.CommonSpringBean
import com.intellij.spring.model.SpringBeanPointer
import com.intellij.spring.model.SpringModelSearchParameters
import com.intellij.spring.model.actions.generate.GenerateSpringBeanDependenciesUtil
import com.intellij.spring.model.actions.generate.GenerateSpringBeanDependenciesUtil.*
import com.intellij.spring.model.actions.generate.SpringBeanClassMember
import com.intellij.spring.model.highlighting.SpringConstructorArgResolveUtil
import com.intellij.spring.model.utils.SpringBeanCoreUtils
import com.intellij.spring.model.utils.SpringBeanUtils
import com.intellij.spring.model.utils.SpringModelSearchers
import com.intellij.spring.model.utils.SpringModelUtils
import com.intellij.spring.model.xml.DomSpringBean
import com.intellij.spring.model.xml.beans.Beans
import com.intellij.spring.model.xml.beans.SpringBean
import com.intellij.util.IncorrectOperationException
import com.intellij.util.xml.DomElement
import com.intellij.util.xml.DomUtil
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.caches.project.moduleInfo
import org.jetbrains.kotlin.idea.caches.resolve.*
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaOrKotlinMemberDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.util.getParameterDescriptor
import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory
import org.jetbrains.kotlin.idea.completion.InsertHandlerProvider
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject
import org.jetbrains.kotlin.idea.editor.BatchTemplateRunner
import org.jetbrains.kotlin.idea.spring.beanClass
import org.jetbrains.kotlin.idea.spring.effectiveBeanClasses
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.load.java.propertyNameBySetMethodName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.TypeReconstructionUtil
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.ifEmpty
import java.util.*
// TODO: GenerateSpringBeanDependenciesUtil.ensureFileWritable() is not accessible here
private fun DomElement.ensureFileWritable() = ensureFileWritable(DomUtil.getFile(this).virtualFile, manager.project)
// TODO: GenerateSpringBeanDependenciesUtil.getReferenceName() is not accessible here
private fun getReferencedName(currentBean: SpringBean, bean: SpringBeanPointer<CommonSpringBean>): String? {
val model = SpringModelUtils.getInstance().getSpringModel(currentBean)
return SpringBeanCoreUtils.getReferencedName(bean, model.allCommonBeans)
}
// TODO: GenerateSpringBeanDependenciesUtil.getExistedSetter() is not accessible here
private fun getExistedSetter(currentBeanClass: PsiClass, setterPsiClass: PsiClass): PsiMethod? {
val psiClassType = JavaPsiFacade.getInstance(setterPsiClass.project).elementFactory.createType(setterPsiClass)
return currentBeanClass.allMethods.firstOrNull {
PropertyUtil.isSimplePropertySetter(it) && it.parameterList.parameters.first().type.isAssignableFrom(psiClassType)
}
}
// TODO: GenerateSpringBeanDependenciesUtil.findConstructor() is not accessible here
private fun findConstructor(constructors: Array<PsiMethod>, psiParameterTypes: List<PsiType>): PsiMethod? {
return constructors.firstOrNull {
val parameters = it.parameterList.parameters
if (it.parameterList.parametersCount != psiParameterTypes.size) return@firstOrNull false
(psiParameterTypes zip parameters).all { it.first.isAssignableFrom(it.second.type) }
}
}
// TODO: GenerateSpringBeanDependenciesUtil.findExistedConstructor() is not accessible here
private fun findExistedConstructor(
currentBean: SpringBean,
currentBeanClass: PsiClass,
candidateParameterClasses: Array<out PsiClass>
): PsiMethod? {
val constructors = SpringConstructorArgResolveUtil.findMatchingMethods(currentBean)
for (candidateBeanClass in candidateParameterClasses) {
for (constructor in constructors) {
val psiParameterTypes = SmartList<PsiType>().apply {
constructor.parameterList.parameters.mapTo(this) { it.type }
add(PsiTypesUtil.getClassType(candidateBeanClass))
}
findConstructor(currentBeanClass.constructors, psiParameterTypes)?.let { return it }
}
}
return null
}
// TODO: GenerateSpringBeanDependenciesUtil.createSpringBean() is not accessible here
private fun createSpringBean(parentBeans: Beans, psiClass: PsiClass): SpringBean? {
if (!((parentBeans as DomElement)).ensureFileWritable()) return null
return parentBeans.addBean().apply {
clazz.stringValue = psiClass.qualifiedName
id.stringValue = SpringBeanCoreUtils.suggestBeanNames(this).firstOrNull() ?: ""
}
}
private fun SpringBean.getFactoryFunctionName(factoryBeanClass: KtClass): String {
factoryMethod.stringValue?.let { if (!it.isBlank()) return it }
val existingNames = factoryBeanClass.declarations.mapNotNull { if (it is KtFunction || it is KtClassOrObject) it.name else null }
return KotlinNameSuggester.suggestNameByName("create${beanClass()!!.name}") { it !in existingNames }
}
internal val PsiClass.defaultTypeText: String
get() {
val qName = qualifiedName ?: return "Any"
val typeParameters = typeParameters
if (typeParameters.isEmpty()) return qName
return TypeReconstructionUtil.getTypeNameAndStarProjectionsString(qName, typeParameters.size)
}
private fun createSettable(
candidateBean: SpringBeanPointer<CommonSpringBean>,
currentBeanClass: KtLightClass,
candidateBeanClasses: Array<out PsiClass>,
injectionKind: SpringDependencyInjectionKind
): KtNamedDeclaration {
val project = currentBeanClass.project
val psiFactory = KtPsiFactory(project)
val beanName = candidateBean.name
try {
val candidateClass = candidateBeanClasses.first()
val propertyName = if (beanName != null && beanName.isIdentifier()) beanName else candidateClass.name!!
val prototype: KtNamedDeclaration = when (injectionKind) {
SpringDependencyInjectionKind.SETTER -> {
psiFactory.createFunction("fun set${propertyName.capitalize()}(${propertyName.decapitalize()}: ${candidateClass.defaultTypeText}) { }")
}
SpringDependencyInjectionKind.LATEINIT_PROPERTY -> {
psiFactory.createProperty("lateinit var ${propertyName.decapitalize()}: ${candidateClass.defaultTypeText}")
}
else -> error("Unexpected injection kind: $injectionKind")
}
return currentBeanClass.kotlinOrigin!!.addDeclaration(prototype).apply { ShortenReferences.DEFAULT.process(this) }
}
catch (e: IncorrectOperationException) {
throw RuntimeException(e)
}
}
internal fun getSuggestedNames(
beanPointer: SpringBeanPointer<CommonSpringBean>,
declaration: KtCallableDeclaration,
excludedInValidator: List<KtDeclaration> = listOf(declaration),
existingNames: Collection<String> = emptyList(),
getType: CallableDescriptor.() -> KotlinType?
): Collection<String> {
val names = LinkedHashSet<String>()
val newDeclarationNameValidator =
NewDeclarationNameValidator(declaration.parent, null, NewDeclarationNameValidator.Target.VARIABLES, excludedInValidator)
fun validate(name: String) = name !in existingNames && newDeclarationNameValidator(name)
SpringBeanUtils.getInstance()
.findBeanNames(beanPointer.springBean)
.asSequence()
.filter { it.isIdentifier() }
.mapTo(names) { KotlinNameSuggester.suggestNameByName(it, ::validate) }
(declaration.resolveToDescriptor() as CallableDescriptor).getType()?.let {
names += KotlinNameSuggester.suggestNamesByType(it, ::validate)
}
return names
}
internal fun TemplateBuilderImpl.appendVariableTemplate(
variable: KtCallableDeclaration,
candidateBeanClasses: Array<out PsiClass>,
computeSuggestions: (() -> Collection<String>)?
) {
if (computeSuggestions != null) {
val suggestedNames = computeSuggestions()
val defaultName = variable.name ?: ""
val nameExpression = object : Expression() {
override fun calculateResult(context: ExpressionContext) = TextResult(defaultName)
override fun calculateQuickResult(context: ExpressionContext) = calculateResult(context)
override fun calculateLookupItems(context: ExpressionContext): Array<LookupElement>? {
PsiDocumentManager.getInstance(context.project).commitAllDocuments()
return suggestedNames.map { LookupElementBuilder.create(it) }.toTypedArray()
}
}
replaceElement(variable.nameIdentifier, "names", nameExpression, true)
}
val superTypeVariants = getSuperTypeVariants(candidateBeanClasses)
if (superTypeVariants.size > 1) {
val lookupFactory = BasicLookupElementFactory(variable.project, InsertHandlerProvider(CallType.TYPE) { emptyList() })
val typeReferenceExpression = object : Expression() {
override fun calculateResult(context: ExpressionContext) = TextResult(candidateBeanClasses.first().qualifiedName ?: "")
override fun calculateQuickResult(context: ExpressionContext) = calculateResult(context)
override fun calculateLookupItems(context: ExpressionContext): Array<LookupElement>? {
return superTypeVariants
.mapTo(LinkedHashSet()) { lookupFactory.createLookupElementForJavaClass(it, qualifyNestedClasses = true) }
.toTypedArray()
}
}
replaceElement(variable.typeReference, "type", typeReferenceExpression, true)
}
}
private fun BatchTemplateRunner.addCreateFunctionTemplate(
function: KtFunction,
candidateBeanClasses: Map<Int, Array<out PsiClass>>,
dependency: SpringBeanPointer<CommonSpringBean>
) {
val parameterList = function.valueParameterList!!
val builder = TemplateBuilderImpl(parameterList)
addTemplateFactory(parameterList) {
for ((paramIndex, candidateBeanClassesForParam) in candidateBeanClasses) {
builder.appendVariableTemplate(function.valueParameters[paramIndex], candidateBeanClassesForParam) {
getSuggestedNames(dependency, function, listOf(function.valueParameters[paramIndex], function)) {
valueParameters[paramIndex].type
}
}
}
builder.buildInlineTemplate()
}
}
private fun BatchTemplateRunner.addCreatePropertyTemplate(property: KtProperty, candidateBeanClasses: Array<out PsiClass>) {
val builder = TemplateBuilderImpl(property)
addTemplateFactory(property) {
builder.appendVariableTemplate(property, candidateBeanClasses, null)
builder.buildInlineTemplate()
}
}
private fun BatchTemplateRunner.addCreateFunctionTemplate(
function: KtFunction,
paramIndex: Int,
candidateBeanClasses: Array<out PsiClass>,
dependency: SpringBeanPointer<CommonSpringBean>
) {
addCreateFunctionTemplate(function, mapOf(paramIndex to candidateBeanClasses), dependency)
}
private fun getOrCreateSetter(
candidateBean: SpringBeanPointer<CommonSpringBean>,
currentBeanClass: KtLightClass,
candidateBeanClasses: Array<out PsiClass>,
templatesHolder: BatchTemplateRunner,
injectionKind: SpringDependencyInjectionKind
): PsiNamedElement? {
for (candidateBeanClass in candidateBeanClasses) {
getExistedSetter(currentBeanClass, candidateBeanClass)?.let { return it }
}
if (!ensureFileWritable(currentBeanClass)) return null
val settable = createSettable(candidateBean, currentBeanClass, candidateBeanClasses, injectionKind)
when (settable) {
is KtNamedFunction -> templatesHolder.addCreateFunctionTemplate(settable, 0, candidateBeanClasses, candidateBean)
is KtProperty -> templatesHolder.addCreatePropertyTemplate(settable, candidateBeanClasses)
}
return settable
}
private fun createSetterDependency(
currentBean: SpringBean,
dependency: SpringBeanPointer<CommonSpringBean>,
injectionKind: SpringDependencyInjectionKind
): BatchTemplateRunner? {
val templatesHolder = BatchTemplateRunner(currentBean.manager.project)
val currentBeanClass = currentBean.beanClass() as? KtLightClass ?: return null
val candidateBeanClasses = dependency.effectiveBeanClasses().ifEmpty { return null }
val setter = getOrCreateSetter(dependency, currentBeanClass, candidateBeanClasses, templatesHolder, injectionKind) ?: return null
currentBean.addProperty().apply {
name.ensureXmlElementExists()
name.stringValue = when (setter) {
is PsiMethod, is KtFunction -> propertyNameBySetMethodName(Name.identifier(setter.name!!), false)!!.asString()
else -> setter.name
}
refAttr.setStringValue(getReferencedName(currentBean, dependency))
}
return templatesHolder
}
private fun addConstructorParameter(
currentBeanClass: KtLightClass,
candidateBeanClass: PsiClass,
constructor: KtFunction
) {
val psiFactory = KtPsiFactory(currentBeanClass)
val validator = NewDeclarationNameValidator(constructor, null, NewDeclarationNameValidator.Target.VARIABLES)
val resolutionFacade = currentBeanClass.kotlinOrigin!!.getResolutionFacade()
val defaultType = (candidateBeanClass.getJavaOrKotlinMemberDescriptor(resolutionFacade) as ClassDescriptor).defaultType
val name = KotlinNameSuggester.suggestNamesByType(defaultType, validator).first()
val parameter = psiFactory.createParameter("$name: ${candidateBeanClass.defaultTypeText}")
constructor.getValueParameterList()!!.addParameter(parameter).apply { ShortenReferences.DEFAULT.process(this) }
}
private fun findProperConstructorAndAddParameter(
currentBean: SpringBean,
dependency: SpringBeanPointer<CommonSpringBean>,
currentBeanClass: KtLightClass,
candidateBeanClass: PsiClass,
holder: BatchTemplateRunner
): PsiMethod? {
val lightConstructor = currentBean.resolvedConstructorArgs.resolvedMethod as? KtLightMethod ?: return null
val constructorOrigin = lightConstructor.kotlinOrigin
val properConstructor = when (constructorOrigin) {
is KtFunction -> constructorOrigin
is KtClass -> constructorOrigin.createPrimaryConstructorIfAbsent()
else -> return null
}
addConstructorParameter(currentBeanClass, candidateBeanClass, properConstructor)
holder.addCreateFunctionTemplate(properConstructor, properConstructor.getValueParameters().lastIndex, arrayOf(candidateBeanClass), dependency)
return lightConstructor
}
private fun createConstructorWithTemplate(
currentBean: SpringBean,
dependency: SpringBeanPointer<CommonSpringBean>,
templatesHolder: BatchTemplateRunner
) {
val beanClass = currentBean.beanClass() as? KtLightClass ?: return
val ktBeanClass = beanClass.kotlinOrigin as? KtClass ?: return
try {
val psiFactory = KtPsiFactory(beanClass)
val factoryFunction = when {
currentBean.factoryBean.exists() -> {
// TODO: Support non-Kotlin factory beans
val factoryClass = currentBean.factoryBean.value?.beanClass as? KtLightClass ?: return
val ktFactoryClass = factoryClass.kotlinOrigin as? KtClass ?: return
val funName = currentBean.getFactoryFunctionName(ktFactoryClass)
val funText = "fun $funName(): ${beanClass.defaultTypeText} {\n return null \n}"
ktFactoryClass.addDeclaration(psiFactory.createFunction(funText))
}
currentBean.factoryMethod.exists() -> {
val funName = currentBean.getFactoryFunctionName(ktBeanClass)
val funText = "@JvmStatic fun $funName(): ${beanClass.defaultTypeText} {\n return null \n}"
ktBeanClass.getOrCreateCompanionObject().addDeclaration(psiFactory.createFunction(funText))
}
else -> {
if (ktBeanClass.hasPrimaryConstructor() || ktBeanClass.getSecondaryConstructors().isNotEmpty()) {
ktBeanClass.addDeclaration(psiFactory.createSecondaryConstructor("constructor()"))
}
else {
ktBeanClass.createPrimaryConstructorIfAbsent()
}
}
} as KtFunction
ShortenReferences.DEFAULT.process(factoryFunction)
val dummyMethod = JavaPsiFacade.getElementFactory(beanClass.project).createMethod("foo", PsiType.VOID)
dummyMethod.containingFile.moduleInfo = ktBeanClass.getModuleInfo()
SpringConstructorArgResolveUtil.suggestParamsForConstructorArgs(currentBean).forEach { dummyMethod.parameterList.add(it) }
val parameterList = factoryFunction.valueParameterList!!
val indexOffset = parameterList.parameters.size
val candidateClassMap = SmartHashMap<Int, Array<PsiClass>>()
val resolutionFacade = ktBeanClass.getResolutionFacade()
for ((i, psiParam) in dummyMethod.parameterList.parameters.withIndex()) {
val descriptor = psiParam.getParameterDescriptor(resolutionFacade)!!
val ktParameter = psiFactory.createParameter(
"${psiParam.name}: ${IdeDescriptorRenderers.SOURCE_CODE_NOT_NULL_TYPE_APPROXIMATION.renderType(descriptor.type)}"
)
parameterList.addParameter(ktParameter)
(psiParam.type as? PsiClassType)?.resolve()?.let { candidateClassMap[i + indexOffset] = arrayOf(it) }
}
templatesHolder.addCreateFunctionTemplate(factoryFunction, candidateClassMap, dependency)
}
catch (e: IncorrectOperationException) {
throw RuntimeException(e)
}
}
private fun createConstructorDependency(
currentBean: SpringBean,
dependency: SpringBeanPointer<CommonSpringBean>
): BatchTemplateRunner? {
val templatesHolder = BatchTemplateRunner(currentBean.manager.project)
val currentBeanClass = currentBean.beanClass() as? KtLightClass ?: return null
val candidateBeanClasses = dependency.effectiveBeanClasses().ifEmpty { return null }
var existedConstructor = findExistedConstructor(currentBean, currentBeanClass, candidateBeanClasses)
if (existedConstructor == null) {
if (!ensureFileWritable(currentBeanClass)) return null
existedConstructor = findProperConstructorAndAddParameter(currentBean, dependency, currentBeanClass, candidateBeanClasses.first(), templatesHolder)
}
val newConstructorArg = currentBean.addConstructorArg()
newConstructorArg.refAttr.stringValue = getReferencedName(currentBean, dependency)
if (existedConstructor == null && SpringConstructorArgResolveUtil.findMatchingMethods(currentBean).isEmpty()) {
createConstructorWithTemplate(currentBean, dependency, templatesHolder)
}
return templatesHolder
}
private fun generateDependency(
springBean: SpringBean,
dependency: SpringBeanPointer<CommonSpringBean>,
injectionKind: SpringDependencyInjectionKind
): BatchTemplateRunner? {
return runWriteAction {
if (injectionKind.isSetter) {
createSetterDependency(springBean, dependency, injectionKind)
}
else {
createConstructorDependency(springBean, dependency)
}
}
}
enum class SpringDependencyInjectionKind(val isSetter: Boolean) {
CONSTRUCTOR(false),
SETTER(true),
LATEINIT_PROPERTY(true)
}
@set:TestOnly
var Project.beanFilter: (SpringBeanPointer<CommonSpringBean>) -> Boolean
by NotNullableUserDataProperty(Key.create("BEAN_CHOOSER")) { true }
@NotNull
private fun chooseDependentBeans(
candidates: Set<SpringBeanClassMember>,
project: Project,
isSetterDependency: Boolean
): List<SpringBeanPointer<CommonSpringBean>> {
return if (ApplicationManager.getApplication().isUnitTestMode) {
candidates.map { it.springBean }.filter(project.beanFilter)
}
else {
GenerateSpringBeanDependenciesUtil.chooseDependentBeans(candidates, project, isSetterDependency)
}
}
fun generateDependenciesFor(
springModel: CommonSpringModel,
klass: KtLightClass,
injectionKind: SpringDependencyInjectionKind
): List<BatchTemplateRunner> {
val project = klass.project
val isSetter = injectionKind.isSetter
SpringModelSearchers
.findBeans(springModel, SpringModelSearchParameters.byClass(klass))
.asSequence()
.map { it.springBean }
.filterIsInstance<SpringBean>()
.firstOrNull { acceptBean(it, false) }
?.let { springBean ->
if (!springBean.ensureFileWritable()) return emptyList()
if (springBean.beanClass() == null) return emptyList()
val dependencies = chooseDependentBeans(getCandidates(springBean, isSetter), project, isSetter)
return dependencies.mapNotNull { generateDependency(springBean, it, injectionKind) }
}
val dependencies = chooseDependentBeans(getCandidates(springModel, klass, isSetter), project, isSetter)
val beanXml = dependencies.singleOrNull()?.springBean as? DomSpringBean ?: return emptyList()
val beansXml = beanXml.getParentOfType(Beans::class.java, false) ?: return emptyList()
val newBean = createSpringBean(beansXml, klass) ?: return emptyList()
return dependencies.mapNotNull { generateDependency(newBean, it, injectionKind) }
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.idea.spring.inspections
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import com.intellij.spring.model.highlighting.SpringJavaAutowiredMembersInspection
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.inspections.registerWithElementsUnwrapped
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtVisitorVoid
class SpringKotlinAutowiredMembersInspection : AbstractKotlinInspection() {
private val javaInspection by lazy { SpringJavaAutowiredMembersInspection() }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object: KtVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
val lightClass = classOrObject.toLightClass() ?: return
javaInspection.checkClass(lightClass, holder.manager, isOnTheFly)?.registerWithElementsUnwrapped(holder, isOnTheFly)
}
}
}
}
@@ -16,8 +16,8 @@ import com.intellij.spring.CommonSpringModel
import com.intellij.spring.SpringBundle
import com.intellij.spring.model.SpringBeanPointer
import com.intellij.spring.model.converters.SpringConverterUtil
import com.intellij.spring.model.highlighting.autowire.SpringJavaInjectionPointsAutowiringInspection
import com.intellij.spring.model.utils.SpringAutowireUtil
import com.intellij.spring.model.highlighting.SpringAutowireUtil
import com.intellij.spring.model.highlighting.SpringJavaAutowiringInspection
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightClass
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
class SpringKotlinAutowiringInspection : AbstractKotlinInspection() {
// Based on SpringJavaInjectionPointsAutowiringInspection.AddSpringBeanQualifierFix
// Based on SpringJavaAutowiringInspection.AddSpringBeanQualifierFix
class AddQualifierFix(
modifierListOwner: KtModifierListOwner,
private val beanPointers: Collection<SpringBeanPointer<*>>,
@@ -86,16 +86,16 @@ class SpringKotlinAutowiringInspection : AbstractKotlinInspection() {
}
}
private val javaInspection by lazy { SpringJavaInjectionPointsAutowiringInspection() }
private val javaInspection by lazy { SpringJavaAutowiringInspection() }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
// TODO: SpringJavaInjectionPointsAutowiringInspection.checkAutowiredMethod() is not accessible here
// TODO: SpringJavaAutowiringInspection.checkAutowiredMethod() is not accessible here
private fun checkAutowiredMethod(psiMethod: PsiMethod, holder: ProblemsHolder, springModel: CommonSpringModel, required: Boolean) {
val resourceAnnotation = SpringAutowireUtil.getResourceAnnotation(psiMethod)
when {
resourceAnnotation != null -> {
val propertyType = PropertyUtil.getPropertyType(psiMethod) ?: return
SpringJavaInjectionPointsAutowiringInspection.checkInjectionPoint(psiMethod, propertyType, holder, springModel, required)
SpringJavaAutowiringInspection.checkAutowiredPsiMember(psiMethod, propertyType, holder, springModel, required)
}
psiMethod.parameterList.parametersCount == 0 &&
SpringAutowireUtil.isAutowiredByAnnotation(psiMethod) -> {
@@ -107,7 +107,7 @@ class SpringKotlinAutowiringInspection : AbstractKotlinInspection() {
else -> {
for (parameter in psiMethod.parameterList.parameters) {
if (AnnotationUtil.isAnnotated(parameter, "org.springframework.beans.factory.annotation.Value", true)) continue
SpringJavaInjectionPointsAutowiringInspection.checkInjectionPoint(parameter, parameter.type, holder, springModel, required)
SpringJavaAutowiringInspection.checkAutowiredPsiMember(parameter, parameter.type, holder, springModel, required)
}
}
}
@@ -153,7 +153,7 @@ class SpringKotlinAutowiringInspection : AbstractKotlinInspection() {
private fun PsiField.processLightField() {
if (!SpringAutowireUtil.isAutowiredByAnnotation(this)) return
processLightMember { holder, model, required ->
SpringJavaInjectionPointsAutowiringInspection.checkInjectionPoint(this, type, holder, model, required)
SpringJavaAutowiringInspection.checkAutowiredPsiMember(this, type, holder, model, required)
}
}
@@ -20,8 +20,8 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.psi.*
import com.intellij.spring.model.DefaultSpringBeanQualifier
import com.intellij.spring.model.converters.SpringConverterUtil
import com.intellij.spring.model.highlighting.SpringAutowireUtil
import com.intellij.spring.model.jam.qualifiers.SpringJamQualifier
import com.intellij.spring.model.utils.SpringAutowireUtil
import com.intellij.spring.model.utils.SpringModelSearchers
import com.intellij.spring.references.SpringQualifierReference
import org.jetbrains.kotlin.asJava.elements.KtLightElement