Remove 172 bunchset

This commit is contained in:
Vyacheslav Gerasimov
2018-08-02 19:08:04 +03:00
parent 0c867b4804
commit a2bf417d75
338 changed files with 0 additions and 26220 deletions
@@ -1,77 +0,0 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<psi.referenceContributor
language="kotlin"
implementation="org.jetbrains.kotlin.idea.spring.references.KotlinSpringReferenceContributor"
order="before kotlinFilePathReferenceContributor" />
<psi.referenceContributor
language="kotlin"
implementation="org.jetbrains.kotlin.idea.jam.KotlinJamReferenceContributor"
order="before kotlinFilePathReferenceContributor" />
<multiHostInjector implementation="org.jetbrains.kotlin.idea.spring.el.KotlinSpringELInjector"/>
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.spring.lineMarking.KotlinSpringClassAnnotator"/>
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.spring.lineMarking.KotlinSpringModelDependenciesLineMarkerProvider"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.spring.inspections.KotlinFinalClassOrFunSpringInspection"
displayName="Final Kotlin class or function with Spring annotation"
groupBundle="resources.messages.SpringBundle"
groupKey="inspection.group.code"
groupPath="Spring,Spring Core"
enabledByDefault="false"
level="WARNING"/>
<localInspection language="kotlin"
displayName="Spring Facet Code Configuration (Kotlin)"
groupBundle="resources.messages.SpringBundle"
groupKey="inspection.group.setup"
groupPath="Spring,Spring Core"
enabledByDefault="true"
level="WARNING"
implementationClass="org.jetbrains.kotlin.idea.spring.inspections.KotlinSpringFacetCodeInspection"/>
<localInspection language="kotlin"
displayName="Autowired members defined in invalid Spring bean (Kotlin)"
groupBundle="resources.messages.SpringBundle"
groupKey="inspection.group.code"
groupPath="Spring,Spring Core"
enabledByDefault="true"
level="WARNING"
implementationClass="org.jetbrains.kotlin.idea.spring.inspections.SpringKotlinAutowiredMembersInspection"/>
<localInspection language="kotlin"
displayName="Autowiring for Bean Class (Kotlin)"
groupBundle="resources.messages.SpringBundle"
groupKey="inspection.group.code"
groupPath="Spring,Spring Core"
enabledByDefault="true"
level="ERROR"
implementationClass="org.jetbrains.kotlin.idea.spring.inspections.SpringKotlinAutowiringInspection"/>
<localInspection language="kotlin"
displayName="@ComponentScan setup (Kotlin)"
groupBundle="resources.messages.SpringBundle"
groupKey="inspection.group.code"
groupPath="Spring,Spring Core"
enabledByDefault="true"
level="ERROR"
implementationClass="org.jetbrains.kotlin.idea.spring.inspections.KotlinSpringComponentScanInspection"/>
</extensions>
<actions>
<action id="Kotlin.Spring.Beans.Generate.Setter.Dependency.Action"
class="org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanXmlDependencyAction$Setter">
<add-to-group anchor="last" group-id="GenerateGroup"/>
</action>
<action id="Kotlin.Spring.Beans.Generate.Constructor.Dependency.Action"
class="org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanXmlDependencyAction$Constructor">
<add-to-group anchor="last" group-id="GenerateGroup"/>
</action>
<action id="Kotlin.Spring.Beans.Generate.Lateinit.Property.Dependency.Action"
class="org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanXmlDependencyAction$LateinitProperty">
<add-to-group anchor="last" group-id="GenerateGroup"/>
</action>
<action id="Kotlin.Spring.Beans.Generate.Autowired.Dependency.Action"
class="org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinAutowiredDependencyAction">
<add-to-group anchor="last" group-id="GenerateGroup"/>
</action>
</actions>
</idea-plugin>
@@ -1,57 +0,0 @@
package org.jetbrains.kotlin.idea.jam
import com.intellij.codeInsight.completion.CompletionUtil
import com.intellij.jam.JamService
import com.intellij.jam.JamStringAttributeElement
import com.intellij.jam.reflect.JamStringAttributeMeta
import com.intellij.psi.PsiElementRef
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceRegistrar
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.references.AbstractKotlinReferenceContributor
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.psiUtil.isPlain
import org.jetbrains.kotlin.psi.psiUtil.parents
// Based on the JamReferenceContributor
class KotlinJamReferenceContributor : AbstractKotlinReferenceContributor() {
@Suppress("UNCHECKED_CAST")
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerMultiProvider<KtStringTemplateExpression> { expression ->
if (!expression.isPlain()) return@registerMultiProvider PsiReference.EMPTY_ARRAY
val argument = expression
.parents
.filterIsInstance<KtValueArgument>()
.firstOrNull() { it.parent?.parent is KtAnnotationEntry } ?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val annotationEntry = argument.parent.parent as KtAnnotationEntry
val lightAnnotation = (CompletionUtil.getOriginalOrSelf(annotationEntry)).toLightAnnotation()
?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val service = JamService.getJamService(expression.project)
val annotationMeta = service.getMeta(lightAnnotation)
?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val attributeName = argument.getArgumentName()?.asName?.asString()
val attribute = annotationMeta.findAttribute(attributeName) as? JamStringAttributeMeta<Any, Any>
?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val jam = attribute.getJam(PsiElementRef.real(lightAnnotation))
val converter = attribute.converter
when (jam) {
is JamStringAttributeElement<*> -> converter.createReferences(jam as JamStringAttributeElement<Any>)
is List<*> -> {
val list = SmartList<PsiReference>()
for (item in jam) {
val jamElement = item as? JamStringAttributeElement<Any> ?: continue
if (jamElement.psiElement?.unwrapped != expression) continue
list += converter.createReferences(jamElement)
}
list.toTypedArray()
}
else -> PsiReference.EMPTY_ARRAY
}
}
}
}
@@ -1,512 +0,0 @@
/*
* 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.forcedModuleInfo
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.forcedModuleInfo = 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) }
}
@@ -1,34 +0,0 @@
package org.jetbrains.kotlin.idea.spring.inspections
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElementVisitor
import com.intellij.spring.model.highlighting.jam.SpringComponentScanInspection
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 KotlinSpringComponentScanInspection : AbstractKotlinInspection() {
private val javaInspection by lazy {
object : SpringComponentScanInspection() {
// make base method visible
override public fun checkClass(aClass: PsiClass, holder: ProblemsHolder, module: Module) = super.checkClass(aClass, holder, module)
}
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object: KtVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
val module = ModuleUtilCore.findModuleForPsiElement(classOrObject) ?: return
val lightClass = classOrObject.toLightClass() ?: return
val tmpHolder = ProblemsHolder(holder.manager, classOrObject.containingFile, isOnTheFly)
javaInspection.checkClass(lightClass, tmpHolder, module)
tmpHolder.resultsArray.registerWithElementsUnwrapped(holder, isOnTheFly)
}
}
}
}
@@ -1,39 +0,0 @@
/*
* 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)
}
}
}
}
@@ -1,182 +0,0 @@
package org.jetbrains.kotlin.idea.spring.inspections
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.template.*
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.util.PropertyUtil
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.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
import org.jetbrains.kotlin.asJava.toLightElements
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.inspections.registerWithElementsUnwrapped
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
class SpringKotlinAutowiringInspection : AbstractKotlinInspection() {
// Based on SpringJavaAutowiringInspection.AddSpringBeanQualifierFix
class AddQualifierFix(
modifierListOwner: KtModifierListOwner,
private val beanPointers: Collection<SpringBeanPointer<*>>,
private val annotationFqName: String
) : LocalQuickFix {
private val elementPointer = modifierListOwner.createSmartPointer()
override fun getName() = SpringBundle.message("SpringAutowiringInspection.add.qualifier.fix")
override fun getFamilyName() = name
private fun getQualifierNamesSuggestNamesExpression(expression: KtStringTemplateExpression): Expression {
return object : Expression() {
override fun calculateResult(context: ExpressionContext): Result? {
PsiDocumentManager.getInstance(context.project).commitAllDocuments()
return TextResult(expression.plainContent)
}
override fun calculateQuickResult(context: ExpressionContext) = this.calculateResult(context)
override fun calculateLookupItems(context: ExpressionContext): Array<LookupElement>? {
PsiDocumentManager.getInstance(context.project).commitAllDocuments()
return beanPointers.sortedBy { it.name ?: "" }.mapNotNull { SpringConverterUtil.createCompletionVariant(it) }.toTypedArray()
}
}
}
private fun createQualifierNameTemplate(expression: KtStringTemplateExpression): Template {
val builder = TemplateBuilderImpl(expression.containingFile)
builder.replaceRange(
expression.getContentRange().shiftRight(expression.startOffset),
getQualifierNamesSuggestNamesExpression(expression)
)
return builder.buildInlineTemplate()
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val modifierListOwner = elementPointer.element ?: return
if (!FileModificationService.getInstance().preparePsiElementForWrite(modifierListOwner)) return
if (beanPointers.isEmpty()) return
val defaultBeanName = with(beanPointers.first().name) { if (isNullOrBlank()) "Unknown" else this }
val entry = KtPsiFactory(project).createAnnotationEntry("@$annotationFqName(\"$defaultBeanName\")")
val addedEntry = modifierListOwner.addAnnotationEntry(entry)
ShortenReferences.DEFAULT.process(addedEntry)
val stringTemplate = addedEntry.valueArguments.first().getArgumentExpression() as? KtStringTemplateExpression ?: return
val virtualFile = modifierListOwner.containingFile.virtualFile!!
val editor = FileEditorManager.getInstance(project).openTextEditor(OpenFileDescriptor(project, virtualFile, 0), false)!!
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
TemplateManager.getInstance(project).startTemplate(editor, createQualifierNameTemplate(stringTemplate))
}
}
private val javaInspection by lazy { SpringJavaAutowiringInspection() }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() {
// 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
SpringJavaAutowiringInspection.checkAutowiredPsiMember(psiMethod, propertyType, holder, springModel, required)
}
psiMethod.parameterList.parametersCount == 0 &&
SpringAutowireUtil.isAutowiredByAnnotation(psiMethod) -> {
val nameIdentifier = psiMethod.nameIdentifier ?: return
val message = SpringBundle.message("bean.autowiring.by.type.no.parameter.for.autowired.method",
if (psiMethod.isConstructor) "constructor" else "method")
holder.registerProblem(nameIdentifier, message)
}
else -> {
for (parameter in psiMethod.parameterList.parameters) {
if (AnnotationUtil.isAnnotated(parameter, "org.springframework.beans.factory.annotation.Value", true)) continue
SpringJavaAutowiringInspection.checkAutowiredPsiMember(parameter, parameter.type, holder, springModel, required)
}
}
}
}
private fun Array<ProblemDescriptor>.registerAdjustedProblems() {
registerWithElementsUnwrapped(holder, isOnTheFly) { qf, element ->
// Can't access AddSpringBeanQualifierFix class directly
val klass = qf.javaClass
if (!klass.name.endsWith("AddSpringBeanQualifierFix")) return@registerWithElementsUnwrapped qf
try {
val fields = klass.declaredFields
@Suppress("UNCHECKED_CAST")
val beanPointers = fields[1].apply { isAccessible = true }.get(qf) as Collection<SpringBeanPointer<*>>
val annotationFqName = fields[2].apply { isAccessible = true }.get(qf) as String
val modifierListOwner = element.getNonStrictParentOfType<KtModifierListOwner>()
?: return@registerWithElementsUnwrapped qf
AddQualifierFix(modifierListOwner, beanPointers, annotationFqName)
}
catch (e: Exception) {
return@registerWithElementsUnwrapped null
}
}
}
private fun <T : PsiMember> T.processLightMember(
action: T.(holder: ProblemsHolder, model: CommonSpringModel, required: Boolean) -> Unit
) {
val model = SpringAutowireUtil.getProcessingSpringModel(containingClass) ?: return
val required = SpringAutowireUtil.isRequired(this)
val tmpHolder = ProblemsHolder(holder.manager, containingFile, isOnTheFly)
action(this, tmpHolder, model, required)
tmpHolder.resultsArray.registerAdjustedProblems()
}
private fun PsiMethod.processLightMethod() {
if (!SpringAutowireUtil.isInjectionPoint(this)) return
processLightMember { holder, model, required -> checkAutowiredMethod(this, holder, model, required) }
}
private fun PsiField.processLightField() {
if (!SpringAutowireUtil.isAutowiredByAnnotation(this)) return
processLightMember { holder, model, required ->
SpringJavaAutowiringInspection.checkAutowiredPsiMember(this, type, holder, model, required)
}
}
private fun PsiClass.processLightClass() {
javaInspection.checkClass(this, holder.manager, isOnTheFly)?.registerAdjustedProblems()
}
override fun visitNamedFunction(function: KtNamedFunction) {
function.toLightMethods().firstOrNull()?.processLightMethod()
}
override fun visitProperty(property: KtProperty) {
if (property.name != null) // It is here because `lightElement.name` returns `<no name provided>` instead of `null` suddenly
for (lightElement in property.toLightElements()) {
when (lightElement) {
is KtLightMethod -> lightElement.processLightMethod()
is KtLightField -> lightElement.processLightField()
}
}
}
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
classOrObject.toLightClass()?.processLightClass()
}
}
}
@@ -1,136 +0,0 @@
/*
* 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.lineMarking
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo
import com.intellij.navigation.GotoRelatedItem
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.openapi.util.NotNullLazyValue
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.spring.gutter.SpringClassAnnotator
import com.intellij.util.Function
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.toLightElements
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
import javax.swing.Icon
class KotlinSpringClassAnnotator : SpringClassAnnotator() {
override fun getElementToProcess(psiElement: PsiElement): PsiElement? {
if (psiElement is KtLightIdentifier) return psiElement.parent
psiElement.getParentOfTypeAndBranch<KtClass> { nameIdentifier }?.let { return it.toLightClass() }
psiElement.getParentOfTypeAndBranch<KtNamedFunction> { nameIdentifier }?.let { function ->
val containingClassOrObject = function.containingClassOrObject
val classForLightMethod = if (containingClassOrObject is KtObjectDeclaration
&& containingClassOrObject.isCompanion()
&& function.resolveToDescriptor().hasJvmStaticAnnotation()) {
containingClassOrObject.containingClassOrObject
}
else {
containingClassOrObject
}
return classForLightMethod?.toLightClass()?.methods?.firstOrNull { (it as? KtLightMethod)?.kotlinOrigin == function }
}
psiElement.getParentOfTypeAndBranch<KtConstructor<*>> { getConstructorKeyword() ?: getValueParameterList() }?.let {
return it.toLightMethods().firstOrNull()
}
psiElement.getParentOfTypeAndBranch<KtProperty> { nameIdentifier }?.let { return it }
psiElement.getParentOfTypeAndBranch<KtParameter> { nameIdentifier }?.let { if (it.valOrVarKeyword != null) return it }
psiElement.getParentOfTypeAndBranch<KtAnnotationEntry> {
(typeReference?.typeElement as? KtUserType)?.referenceExpression?.getReferencedNameElement()
}?.let { return it.toLightAnnotation() }
return null
}
private fun doCollectMarkers(psiElement: PsiElement, result: MutableCollection<in RelatedItemLineMarkerInfo<PsiElement>>) {
if (psiElement is KtProperty || psiElement is KtParameter) {
for (it in (psiElement as KtDeclaration).toLightElements()) {
val nameIdentifier = (it as? PsiNameIdentifierOwner)?.nameIdentifier ?: continue
super.collectNavigationMarkers(nameIdentifier, result)
}
return
}
// Workaround for SpringClassAnnotator
(getElementToProcess(psiElement) as? KtLightAnnotationForSourceEntry)?.let { return super.collectNavigationMarkers(it, result) }
super.collectNavigationMarkers(psiElement, result)
}
// TODO
// Weak references to light elements may be reclaimed by GC after original file is modified causing line markers to misbehave
// This workaround allows reuse of SpringClassAnnotator logic and avoids binding of line markers to light elements
private val toolTipProviderField by lazy { LineMarkerInfo::class.java.getDeclaredField("myTooltipProvider").apply { isAccessible = true } }
private val iconField by lazy { LineMarkerInfo::class.java.getDeclaredField("myIcon").apply { isAccessible = true } }
private val iconAlignmentField by lazy { LineMarkerInfo::class.java.getDeclaredField("myIconAlignment").apply { isAccessible = true } }
private val targetsField by lazy { RelatedItemLineMarkerInfo::class.java.getDeclaredField("myTargets").apply { isAccessible = true } }
override fun collectNavigationMarkers(psiElement: PsiElement, result: MutableCollection<in RelatedItemLineMarkerInfo<PsiElement>>) {
val newItems = SmartList<RelatedItemLineMarkerInfo<PsiElement>>()
doCollectMarkers(psiElement, newItems)
newItems.mapNotNullTo(result) { item ->
val itemElement = item.element
val elementToAnnotate = when (itemElement) {
is KtLightIdentifier -> itemElement.origin
is KtLightElement<*, *> -> itemElement.kotlinOrigin
else -> return@mapNotNullTo item
}
if (elementToAnnotate == null) return@mapNotNullTo null
if (alreadyMarked(result, elementToAnnotate, item.navigationHandler)) return@mapNotNullTo null
@Suppress("UNCHECKED_CAST")
RelatedItemLineMarkerInfo<PsiElement>(
elementToAnnotate,
elementToAnnotate.textRange,
iconField.get(item) as Icon?,
item.updatePass,
toolTipProviderField.get(item) as Function<PsiElement, String>,
item.navigationHandler,
iconAlignmentField.get(item) as GutterIconRenderer.Alignment,
targetsField.get(item) as NotNullLazyValue<Collection<GotoRelatedItem>>
)
}
}
private fun alreadyMarked(result: MutableCollection<in RelatedItemLineMarkerInfo<PsiElement>>,
elementToAnnotate: PsiElement,
navigationHandler: GutterIconNavigationHandler<*>?) =
result.any {
when (it) {
is RelatedItemLineMarkerInfo<*> -> {
it.element == elementToAnnotate && it.navigationHandler == navigationHandler
}
else -> false
}
}
}
@@ -1,157 +0,0 @@
/*
* 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.references
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceBase
import com.intellij.psi.PsiReferenceRegistrar
import com.intellij.spring.constants.SpringAnnotationsConstants
import com.intellij.spring.constants.SpringConstants
import com.intellij.spring.model.utils.resources.SpringResourcesBuilder
import com.intellij.spring.model.utils.resources.SpringResourcesUtil
import com.intellij.spring.references.SpringBeanNamesReferenceProvider
import com.intellij.spring.references.SpringBeanReference
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.AbstractKotlinReferenceContributor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getContentRange
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isPlain
import org.jetbrains.kotlin.psi.psiUtil.plainContent
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeAsSequence
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
// TODO: Use Kotlin patterns
class KotlinSpringReferenceContributor : AbstractKotlinReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerProvider<KtStringTemplateExpression> {
if (!it.isPlain()) return@registerProvider null
val callExpression = (it.parent as? KtValueArgument)?.getStrictParentOfType<KtCallExpression>() ?: return@registerProvider null
val context = callExpression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = callExpression.getResolvedCall(context) ?: return@registerProvider null
val callable = resolvedCall.resultingDescriptor as? CallableMemberDescriptor ?: return@registerProvider null
if (!callable.overriddenTreeAsSequence(true).any {
it.containingDeclaration.importableFqName?.asString() == SpringConstants.BEAN_FACTORY_CLASS
}) return@registerProvider null
if (callable.name.asString() !in SpringBeanNamesReferenceProvider.METHODS) return@registerProvider null
SpringBeanReference(it, it.getContentRange())
}
registrar.registerProvider<KtStringTemplateExpression>(PsiReferenceRegistrar.HIGHER_PRIORITY) {
if (!it.isPlain()) return@registerProvider null
val argument = it.parent as? KtValueArgument ?: return@registerProvider null
val argumentName = argument.getArgumentName() ?: return@registerProvider null
if (argumentName.asName.asString() != "name") return@registerProvider null
val entry = argument.getStrictParentOfType<KtAnnotationEntry>() ?: return@registerProvider null
val context = entry.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = entry.getResolvedCall(context) ?: return@registerProvider null
val annotation = (resolvedCall.resultingDescriptor as? ConstructorDescriptor)?.containingDeclaration ?: return@registerProvider null
if (annotation.importableFqName?.asString() != SpringAnnotationsConstants.JAVAX_RESOURCE) return@registerProvider null
val contentRange = it.getContentRange()
var startOffset = contentRange.startOffset
val isFactoryBeanRef: Boolean
if (it.plainContent.startsWith("&")) {
startOffset++
isFactoryBeanRef = true
}
else {
isFactoryBeanRef = false
}
val callable = (it.getStrictParentOfType<KtAnnotationEntry>()?.parent as? KtModifierList)?.parent as? KtCallableDeclaration
val callableType = (callable?.resolveToDescriptor() as? CallableDescriptor)?.returnType
val requiredSuperClass = callableType?.constructor?.declarationDescriptor?.source?.getPsi() as? KtClass
SpringBeanReference(it, TextRange(startOffset, contentRange.endOffset), requiredSuperClass?.toLightClass(), isFactoryBeanRef)
}
registrar.registerProvider<KtStringTemplateExpression>(PsiReferenceRegistrar.HIGHER_PRIORITY) {
if (!it.isPlain()) return@registerProvider null
val argument = it.parent as? KtValueArgument ?: return@registerProvider null
val argumentName = argument.getArgumentName()
if (argumentName != null && argumentName.asName.asString() != "value") return@registerProvider null
val entry = argument.getStrictParentOfType<KtAnnotationEntry>() ?: return@registerProvider null
val context = entry.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = entry.getResolvedCall(context) ?: return@registerProvider null
val annotation = (resolvedCall.resultingDescriptor as? ConstructorDescriptor)?.containingDeclaration ?: return@registerProvider null
if (annotation.importableFqName?.asString() != SpringAnnotationsConstants.SCOPE) return@registerProvider null
KtSpringBeanScopeReference(it)
}
registrar.registerMultiProvider<KtStringTemplateExpression> {
if (!it.isPlain()) return@registerMultiProvider PsiReference.EMPTY_ARRAY
val callExpression = (it.parent as? KtValueArgument)?.getStrictParentOfType<KtCallExpression>()
?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val context = callExpression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = callExpression.getResolvedCall(context) ?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val classDescriptor = (resolvedCall.resultingDescriptor as? ConstructorDescriptor)?.containingDeclaration
?: return@registerMultiProvider PsiReference.EMPTY_ARRAY
val qName = classDescriptor.importableFqName?.asString()
if (qName != SpringConstants.CLASS_PATH_XML_APP_CONTEXT && qName != SpringConstants.CLASS_PATH_RESOURCE) {
return@registerMultiProvider PsiReference.EMPTY_ARRAY
}
val content = it.plainContent
val resourcesBuilder = SpringResourcesBuilder.create(it, content).fromRoot(content.startsWith("/")).soft(false)
SpringResourcesUtil.getInstance().getClassPathReferences(resourcesBuilder)
}
registrar.registerProvider<KtStringTemplateExpression>(PsiReferenceRegistrar.HIGHER_PRIORITY) {
if (!it.isPlain()) return@registerProvider null
val argument = it.parent as? KtValueArgument ?: return@registerProvider null
val argumentName = argument.getArgumentName()
if (argumentName != null && argumentName.asName.asString() != "value") return@registerProvider null
val entry = argument.getStrictParentOfType<KtAnnotationEntry>() ?: return@registerProvider null
val bindingContext = entry.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = entry.getResolvedCall(bindingContext) ?: return@registerProvider null
val annotation = (resolvedCall.resultingDescriptor as? ConstructorDescriptor)?.containingDeclaration
?: return@registerProvider null
if (annotation.importableFqName?.asString() != SpringAnnotationsConstants.QUALIFIER) return@registerProvider null
val annotated = entry.getStrictParentOfType<KtModifierListOwner>() ?: return@registerProvider null
if (annotated is KtClassOrObject) {
object : PsiReferenceBase<KtStringTemplateExpression>(it) {
override fun resolve() = entry
override fun getVariants(): Array<Any> = arrayOf()
}
}
else {
KtSpringQualifierReference(it)
}
}
}
}
@@ -1,44 +0,0 @@
/*
* 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.references
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.ide.TypePresentationService
import com.intellij.psi.ElementManipulators
import com.intellij.psi.PsiReferenceBase
import com.intellij.spring.model.scope.SpringBeanScope
import com.intellij.spring.model.scope.SpringBeanScopeManager
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.plainContent
class KtSpringBeanScopeReference(
element: KtStringTemplateExpression
) : PsiReferenceBase<KtStringTemplateExpression>(element, ElementManipulators.getManipulator(element).getRangeInElement(element)) {
private fun getScopes(): Sequence<SpringBeanScope> {
return SpringBeanScope.getDefaultScopes().asSequence() + SpringBeanScopeManager.getInstance().getCustomBeanScopes(element)
}
private fun getLookupElement(scope: String): LookupElementBuilder {
return LookupElementBuilder
.create(scope)
.withIcon(TypePresentationService.getService().getTypeIcon(SpringBeanScope::class.java))
}
override fun resolve() = if (element.plainContent in getScopes().map { it.value }) element else null
override fun getVariants() = getScopes().map { getLookupElement(it.value) }.toList().toTypedArray()
}
@@ -1,127 +0,0 @@
/*
* 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.references
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.SpringModelSearchers
import com.intellij.spring.references.SpringQualifierReference
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.asJava.toLightElements
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.search.allScope
import org.jetbrains.kotlin.idea.spring.springModel
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.plainContent
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.util.*
// Based on com.intellij.spring.references.SpringQualifierReference
// It inherits from SpringQualifierReference to allow reuse of existing Java inspections which explicitly check for SpringQualifierReference
class KtSpringQualifierReference(element: KtStringTemplateExpression) : SpringQualifierReference(StringTemplatePsiLiteralWrapper(element)) {
class StringTemplatePsiLiteralWrapper(
val element: KtStringTemplateExpression
) : PsiElement by element, PsiLiteralExpression, KtLightElement<KtStringTemplateExpression, KtStringTemplateExpression>, PsiCompiledElement {
override fun getValue() = element.plainContent
override fun getType() = PsiType.getJavaLangString(element.manager, element.project.allScope())
override val clsDelegate: KtStringTemplateExpression
get() = element
override val kotlinOrigin: KtStringTemplateExpression
get() = element
override fun getMirror() = null
}
private fun getAnnotationFqName(): String? {
val annotationEntry = element.getStrictParentOfType<KtAnnotationEntry>() ?: return null
val context = annotationEntry.analyze(BodyResolveMode.PARTIAL)
val annotationType = context[BindingContext.TYPE, annotationEntry.typeReference] ?: return null
return annotationType.constructor.declarationDescriptor?.importableFqName?.asString()
}
override fun multiResolve(incompleteCode: Boolean): Array<out ResolveResult> {
val annotationEntry = element.getStrictParentOfType<KtAnnotationEntry>() ?: return ResolveResult.EMPTY_ARRAY
val lightAnnotation = annotationEntry.toLightAnnotation() ?: return ResolveResult.EMPTY_ARRAY
val springModel = element.springModel ?: return ResolveResult.EMPTY_ARRAY
val results = ArrayList<ResolveResult>()
val jamQualifier = SpringJamQualifier(lightAnnotation, null)
val qualifiedBeans = springModel.findQualifiedBeans(jamQualifier)
qualifiedBeans.mapTo(results) { PsiElementResolveResult(it.springBean.springQualifier!!.identifyingPsiElement) }
val qualifierValue = jamQualifier.qualifierValue
if (qualifierValue != null) {
val beanPointer = SpringModelSearchers.findBean(springModel, qualifierValue)
val psiElement = if (beanPointer != null && beanPointer.isValid) beanPointer.psiElement else null
if (psiElement != null) results += PsiElementResolveResult(psiElement)
}
return results.toTypedArray()
}
// We can't get light elements for property in the synthetic completion KtFile, so look for original one instead
private fun getOriginalProperty(): KtProperty? {
val currentProperty = element.getStrictParentOfType<KtProperty>() ?: return null
val originalFile = element.containingFile.originalFile as? KtFile ?: return null
return originalFile.findElementAt(currentProperty.startOffset)?.getNonStrictParentOfType<KtProperty>()
}
override fun getVariants(): Array<out Any> {
val annotationFqName = getAnnotationFqName() ?: return LookupElement.EMPTY_ARRAY
val springModel = element.springModel ?: return LookupElement.EMPTY_ARRAY
val property = getOriginalProperty() ?: return LookupElement.EMPTY_ARRAY
val lightElement = property.toLightElements().firstOrNull {
it is PsiField || (it is PsiMethod && !it.name.startsWith("set"))
}
val expectedPsiType = when (lightElement) {
is PsiMethod -> lightElement.returnType
is PsiField -> lightElement.type
else -> null
} ?: return LookupElement.EMPTY_ARRAY
return SpringAutowireUtil
.autowireByType(springModel, expectedPsiType)
.mapNotNull {
val qualifier = it.springBean.springQualifier
val value = qualifier?.qualifierValue
when {
qualifier == null ->
SpringConverterUtil.createCompletionVariant(it)
value != null && (qualifier is DefaultSpringBeanQualifier || qualifier.qualifierType?.qualifiedName == annotationFqName) ->
SpringConverterUtil.createCompletionVariant(it, value)
else ->
null
}
}
.toTypedArray()
}
}
@@ -1,2 +0,0 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.spring.inspections.KotlinSpringFacetCodeInspection
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
@@ -1,27 +0,0 @@
// "Add qualifier" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// CONFIGURE_SPRING_FILE_SET
// WITH_RUNTIME
// DISABLE-ERRORS
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.beans.factory.annotation.Qualifier
@Configuration
@ComponentScan
open class InvalidAutowiredByTypeQualifiers
abstract class BaseBarBean
@Component
open class BarBean1 : BaseBarBean()
@Component
open class BarBean2 : BaseBarBean()
@Component
open class MultipleAutowiredCandidates() {
@Autowired
lateinit var <caret>bean: BaseBarBean
}
@@ -1,28 +0,0 @@
// "Add qualifier" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// CONFIGURE_SPRING_FILE_SET
// WITH_RUNTIME
// DISABLE-ERRORS
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.beans.factory.annotation.Qualifier
@Configuration
@ComponentScan
open class InvalidAutowiredByTypeQualifiers
abstract class BaseBarBean
@Component
open class BarBean1 : BaseBarBean()
@Component
open class BarBean2 : BaseBarBean()
@Component
open class MultipleAutowiredCandidates() {
@Qualifier("barBean1")
@Autowired
lateinit var bean: BaseBarBean
}
@@ -1,26 +0,0 @@
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinAutowiredDependencyAction
// CHOOSE_BEAN: barBean, application
package a
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.ComponentScan
@Component
open class FooBean {
@Qualifier("application")
@Autowired
lateinit var application: Application
@Autowired
lateinit var barBean<caret>: BarBean
}
@Component
open class BarBean
@Configuration
@ComponentScan
open class Application
@@ -1,11 +0,0 @@
// ELEMENT: java
// CHAR: \n
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan(basePackages = arrayOf("ja<caret>va"))
open class App {
}
@@ -1,11 +0,0 @@
// ELEMENT: java
// CHAR: \n
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan(basePackages = arrayOf("javava"))
open class App {
}
@@ -1,11 +0,0 @@
// ELEMENT: java
// CHAR: \t
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan(basePackages = arrayOf("ja<caret>va"))
open class App {
}
@@ -1,11 +0,0 @@
// ELEMENT: java
// CHAR: \t
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan(basePackages = arrayOf("java"))
open class App {
}
@@ -1,12 +0,0 @@
// NUMBER: 2
// EXIST: { lookupString:"java" }
// EXIST: { lookupString:"javax" }
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan(basePackages = arrayOf("ja<caret>va"))
open class App {
}
@@ -1,88 +0,0 @@
/*
* 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.tests
import com.intellij.facet.impl.FacetUtil
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiFile
import com.intellij.spring.facet.SpringFacet
import com.intellij.spring.facet.SpringFileSet
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
@Suppress("unused")
class SpringTestFixtureExtension() : TestFixtureExtension {
private var module: Module? = null
override fun setUp(module: Module) {
this.module = module
val springClasspath = System.getProperty("spring.classpath")
?: throw RuntimeException("Unable to get a valid classpath from 'spring.classpath' property, please set it accordingly");
ConfigLibraryUtil.addLibrary(module, "spring", null, springClasspath.split(File.pathSeparator).toTypedArray())
FacetUtil.addFacet(module, SpringFacet.getSpringFacetType())
}
fun configureFileSet(fixture: CodeInsightTestFixture, configFiles: Collection<String>): SpringFileSet {
return configureFileSet(fixture, "default", module!!, configFiles)
}
fun configureFileSet(fixture: CodeInsightTestFixture, id: String, module: Module, configFiles: Collection<String>): SpringFileSet {
return module.getSpringFacetWithAssertion().addFileSet(id, id).apply {
configFiles.forEach { addFile(fixture.copyFileToProject(it)) }
}
}
fun Module.getSpringFacetWithAssertion() = SpringFacet.getInstance(this) ?: error("No Spring facet in ${this}")
override fun tearDown() {
try {
// clear existing SpringFacet configuration before running next test
module?.let { module ->
SpringFacet.getInstance(module)?.let { facet ->
facet.removeFileSets()
FacetUtil.deleteFacet(facet)
}
ConfigLibraryUtil.removeLibrary(module, "spring")
}
}
finally {
module = null
}
}
}
fun SpringTestFixtureExtension.loadConfigByMainFilePath(testPath: String, fixture: JavaCodeInsightTestFixture) {
val mainFileName = PathUtil.getFileName(testPath)
val baseName = FileUtil.getNameWithoutExtension(mainFileName)
val configFileName = if (baseName.endsWith("Xml")) "$baseName-config.xml" else mainFileName
configureFileSet(fixture, listOf(PathUtil.toSystemIndependentName("${PathUtil.getParentPath(testPath)}/$configFileName")))
}
fun configureSpringFileSetByDirective(module: Module, directives: String, psiFiles: Collection<PsiFile>) {
if (!InTextDirectivesUtils.isDirectiveDefined(directives, "// CONFIGURE_SPRING_FILE_SET")) return
val fileSet = SpringFacet.getInstance(module)!!.addFileSet("default", "default")!!
psiFiles.forEach { fileSet.addFile(it.virtualFile) }
}
@@ -1,110 +0,0 @@
/*
* 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.tests.gutter
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.intellij.codeInsight.daemon.LineMarkerProviders
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import icons.SpringApiIcons
import junit.framework.Assert
import junit.framework.AssertionFailedError
import org.jetbrains.kotlin.compatibility.projectDisposableEx
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.jsonUtils.getString
import org.jetbrains.kotlin.idea.spring.lineMarking.KotlinSpringClassAnnotator
import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractSpringClassAnnotatorTest : KotlinLightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST
override fun setUp() {
super.setUp()
TestFixtureExtension.loadFixture<SpringTestFixtureExtension>(myModule)
Assert.assertTrue("Kotlin-ultimate service was not found, make sure that <!-- ULTIMATE-PLUGIN-PLACEHOLDER --> " +
"is replaced in `plugin.xml` with data from `ultimate-plugin.xml`",
LineMarkerProviders.INSTANCE.allForLanguage(KotlinLanguage.INSTANCE).any { it is KotlinSpringClassAnnotator }
)
}
protected fun doTest(path: String) {
val configFilePath = "${KotlinTestUtils.getHomeDirectory()}/$path"
val configFile = File(configFilePath)
val testRoot = configFile.parentFile
val config = JsonParser().parse(FileUtil.loadFile(configFile, true)) as JsonObject
PluginTestCaseBase.addJdk(myFixture.projectDisposableEx, PluginTestCaseBase::mockJdk)
val withRuntime = config["withRuntime"]?.asBoolean ?: false
if (withRuntime) {
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
try {
val springConfigFiles = (config["springConfig"] as JsonArray).map { it.asString }
myFixture.testDataPath = testRoot.absolutePath
for (file in testRoot.listFiles()) {
val name = file.name
if (file.isDirectory) myFixture.copyDirectoryToProject(name, name) else myFixture.configureByFile(name)
}
TestFixtureExtension.getFixture<SpringTestFixtureExtension>()!!.configureFileSet(myFixture, springConfigFiles)
val fileName = config.getString("file")
val iconName = config.getString("icon")
val icon = SpringApiIcons::class.java.getField(iconName)[null]
val gutterMark = myFixture.findGutter(fileName)!!.let {
if (it.icon == icon) it
else myFixture.findGuttersAtCaret().let { gutters ->
gutters.firstOrNull() { it.icon == icon }
?: throw AssertionFailedError("no $icon in gutters: ${gutters.map { it.icon }}")
}
}
val tooltip = config.getString("tooltip")
Assert.assertEquals(tooltip, gutterMark.tooltipText)
val naming = config.getString("naming")
val targets = (config["targets"] as JsonArray).map { it.asString }
when (naming) {
"bean" -> checkBeanGutterTargets(gutterMark, targets)
"property" -> checkBeanPropertyTargets(gutterMark, targets)
"generic" -> checkPsiElementGutterTargets(gutterMark, targets)
else -> error("Unexpected naming: $naming")
}
}
finally {
if (withRuntime) {
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
}
}
override fun tearDown() {
TestFixtureExtension.unloadFixture<SpringTestFixtureExtension>()
super.tearDown()
}
}
@@ -1,14 +0,0 @@
package org.jetbrains.kotlin.idea.spring.tests.inspections
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.codeInsight.AbstractInspectionTest
import org.jetbrains.kotlin.idea.spring.tests.configureSpringFileSetByDirective
abstract class AbstractSpringInspectionTest : AbstractInspectionTest() {
override val forceUsePackageFolder: Boolean = true
override fun configExtra(psiFiles: List<PsiFile>, options: String) {
configureSpringFileSetByDirective(myFixture.module, options, psiFiles)
}
}