Spring Support: "Generate Dependency..." actions
#KT-11407 Fixed
This commit is contained in:
@@ -129,6 +129,7 @@ New features:
|
||||
- [KT-3741](https://youtrack.jetbrains.com/issue/KT-3741) Show Spring-specific line markers on Kotlin classes
|
||||
- [KT-11406](https://youtrack.jetbrains.com/issue/KT-11406) Support Spring EL injections inside of Kotlin string literals
|
||||
- [KT-11604](https://youtrack.jetbrains.com/issue/KT-11604) Support "Configure Spring facet" inspection on Kotlin classes
|
||||
- [KT-11407](https://youtrack.jetbrains.com/issue/KT-11407) Implemented "Generate Spring Dependency..." actions
|
||||
|
||||
Issues fixed:
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ import org.jetbrains.kotlin.idea.refactoring.pushDown.AbstractPushDownTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.rename.AbstractRenameTest
|
||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.AbstractSafeDeleteTest
|
||||
import org.jetbrains.kotlin.idea.resolve.*
|
||||
import org.jetbrains.kotlin.idea.spring.tests.generate.AbstractGenerateSpringDependencyActionTest
|
||||
import org.jetbrains.kotlin.idea.spring.tests.references.AbstractSpringReferenceCompletionHandlerTest
|
||||
import org.jetbrains.kotlin.idea.spring.tests.references.AbstractSpringReferenceCompletionTest
|
||||
import org.jetbrains.kotlin.idea.spring.tests.references.AbstractSpringReferenceNavigationTest
|
||||
@@ -814,6 +815,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractSpringReferenceNavigationTest>() {
|
||||
model("spring/core/references/navigation")
|
||||
}
|
||||
|
||||
testClass<AbstractGenerateSpringDependencyActionTest>() {
|
||||
model("spring/core/generate", pattern = "^([\\w]+)\\.kt$", singleClass = true)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/tests", "compiler/testData") {
|
||||
|
||||
+7
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.load.java.structure.impl.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
||||
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
@@ -82,6 +83,12 @@ fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiParameter.getParameterDescriptor(resolutionFacade: ResolutionFacade? = null): ValueParameterDescriptor? {
|
||||
val method = declarationScope as? PsiMethod ?: return null
|
||||
val methodDescriptor = method.getJavaMethodDescriptor(resolutionFacade) ?: return null
|
||||
return methodDescriptor.valueParameters[parameterIndex()]
|
||||
}
|
||||
|
||||
fun PsiClass.resolveToDescriptor(
|
||||
resolutionFacade: ResolutionFacade,
|
||||
declarationTranslator: (KtClassOrObject) -> KtClassOrObject? = { it }
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.util.*
|
||||
|
||||
@@ -60,14 +62,18 @@ class CollectingNameValidator @JvmOverloads constructor(
|
||||
class NewDeclarationNameValidator(
|
||||
private val visibleDeclarationsContext: KtElement?,
|
||||
private val checkDeclarationsIn: Sequence<PsiElement>,
|
||||
private val target: NewDeclarationNameValidator.Target
|
||||
private val target: NewDeclarationNameValidator.Target,
|
||||
private val excludedDeclarations: List<KtDeclaration> = emptyList()
|
||||
) : (String) -> Boolean {
|
||||
|
||||
constructor(container: PsiElement, anchor: PsiElement?, target: NewDeclarationNameValidator.Target)
|
||||
constructor(container: PsiElement,
|
||||
anchor: PsiElement?,
|
||||
target: NewDeclarationNameValidator.Target,
|
||||
excludedDeclarations: List<KtDeclaration> = emptyList())
|
||||
: this(
|
||||
(anchor ?: container).parentsWithSelf.firstIsInstanceOrNull<KtElement>(),
|
||||
anchor?.siblings() ?: container.allChildren,
|
||||
target)
|
||||
target,
|
||||
excludedDeclarations)
|
||||
|
||||
enum class Target {
|
||||
VARIABLES,
|
||||
@@ -88,6 +94,8 @@ class NewDeclarationNameValidator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun notExcluded(it: DeclarationDescriptorWithSource) = it.source.getPsi() !in excludedDeclarations
|
||||
|
||||
private fun LexicalScope.hasConflict(name: Name): Boolean {
|
||||
fun DeclarationDescriptor.isVisible(): Boolean {
|
||||
return when (this) {
|
||||
@@ -98,14 +106,15 @@ class NewDeclarationNameValidator(
|
||||
|
||||
return when(target) {
|
||||
Target.VARIABLES ->
|
||||
getAllAccessibleVariables(name).any { !it.isExtension && it.isVisible() }
|
||||
getAllAccessibleVariables(name).any { !it.isExtension && it.isVisible() && notExcluded(it) }
|
||||
Target.FUNCTIONS_AND_CLASSES ->
|
||||
getAllAccessibleFunctions(name).any { !it.isExtension && it.isVisible() } ||
|
||||
findClassifier(name, NoLookupLocation.FROM_IDE)?.let { it.isVisible() } ?: false
|
||||
getAllAccessibleFunctions(name).any { !it.isExtension && it.isVisible() && notExcluded(it) } ||
|
||||
findClassifier(name, NoLookupLocation.FROM_IDE)?.let { it.isVisible() && notExcluded(it) } ?: false
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtNamedDeclaration.isConflicting(name: Name): Boolean {
|
||||
if (this in excludedDeclarations) return false
|
||||
if (nameAsName != name) return false
|
||||
if (this is KtCallableDeclaration && receiverTypeReference != null) return false
|
||||
return when(target) {
|
||||
|
||||
@@ -22,4 +22,19 @@
|
||||
level="WARNING"
|
||||
implementationClass="org.jetbrains.kotlin.idea.spring.inspections.KotlinSpringFacetCodeInspection"/>
|
||||
</extensions>
|
||||
|
||||
<actions>
|
||||
<action id="Kotlin.Spring.Beans.Generate.Setter.Dependency.Action"
|
||||
class="org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$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.GenerateKotlinSpringBeanDependencyAction$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.GenerateKotlinSpringBeanDependencyAction$LateinitProperty">
|
||||
<add-to-group anchor="last" group-id="GenerateGroup"/>
|
||||
</action>
|
||||
</actions>
|
||||
</idea-plugin>
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.xml.XmlFile
|
||||
import com.intellij.spring.SpringBundle
|
||||
import com.intellij.spring.SpringManager
|
||||
import com.intellij.spring.model.actions.generate.GenerateSpringBeanDependenciesUtil
|
||||
import com.intellij.spring.model.utils.SpringModelUtils
|
||||
import icons.SpringApiIcons
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.idea.actions.generate.KotlinGenerateActionBase
|
||||
import org.jetbrains.kotlin.idea.editor.BatchTemplateRunner
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
abstract class GenerateKotlinSpringBeanDependencyAction(
|
||||
text: String,
|
||||
private val injectionKind: SpringDependencyInjectionKind
|
||||
) : KotlinGenerateActionBase() {
|
||||
class Constructor: GenerateKotlinSpringBeanDependencyAction(
|
||||
SpringBundle.message("action.Spring.Beans.Generate.Constructor.Dependency.Action.text"),
|
||||
SpringDependencyInjectionKind.CONSTRUCTOR
|
||||
)
|
||||
class Setter: GenerateKotlinSpringBeanDependencyAction(
|
||||
SpringBundle.message("action.Spring.Beans.Generate.Setter.Dependency.Action.text"),
|
||||
SpringDependencyInjectionKind.SETTER
|
||||
)
|
||||
class LateinitProperty: GenerateKotlinSpringBeanDependencyAction(
|
||||
"Spring 'lateinit' Dependency...",
|
||||
SpringDependencyInjectionKind.LATEINIT_PROPERTY
|
||||
)
|
||||
|
||||
init {
|
||||
templatePresentation.text = text
|
||||
templatePresentation.icon = SpringApiIcons.Spring
|
||||
}
|
||||
|
||||
override fun isValidForClass(targetClass: KtClassOrObject): Boolean {
|
||||
if (targetClass !is KtClass || targetClass.isInterface() || targetClass.isEnum() || targetClass.fqName == null) return false
|
||||
val lightClass = targetClass.toLightClass() ?: return false
|
||||
val module = GenerateSpringBeanDependenciesUtil.getSpringModule(lightClass) ?: return false
|
||||
return SpringManager.getInstance(module.project).getCombinedModel(module).configFiles.any { it is XmlFile }
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: PsiFile) {
|
||||
val klass = getTargetClass(editor, file) as? KtClass ?: return
|
||||
val lightClass = klass.toLightClass() ?: return
|
||||
val springModel = SpringModelUtils.getInstance().getPsiClassSpringModel(lightClass)
|
||||
val templates = project.executeWriteCommand<List<BatchTemplateRunner>>("") {
|
||||
generateDependenciesFor(springModel, lightClass, injectionKind)
|
||||
}
|
||||
templates.forEach { it.runTemplates() }
|
||||
}
|
||||
}
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
* 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.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
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.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.KtLightMethod
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
||||
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.getOrCreateCompanionObject
|
||||
import org.jetbrains.kotlin.idea.editor.BatchTemplateRunner
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.load.java.propertyNameBySetMethodName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
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 }
|
||||
}
|
||||
|
||||
private 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 && KotlinNameSuggester.isIdentifier(beanName)) 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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSuggestedNames(
|
||||
beanPointer: SpringBeanPointer<CommonSpringBean>,
|
||||
declaration: KtCallableDeclaration,
|
||||
getType: CallableDescriptor.() -> KotlinType?
|
||||
): Collection<String> {
|
||||
val names = LinkedHashSet<String>()
|
||||
|
||||
val validator = NewDeclarationNameValidator(declaration.parent, null, NewDeclarationNameValidator.Target.VARIABLES, listOf(declaration))
|
||||
|
||||
SpringBeanUtils.getInstance()
|
||||
.findBeanNames(beanPointer.springBean)
|
||||
.asSequence()
|
||||
.filter { KotlinNameSuggester.isIdentifier(it) }
|
||||
.mapTo(names) { KotlinNameSuggester.suggestNameByName(it, validator) }
|
||||
|
||||
(declaration.resolveToDescriptor() as CallableDescriptor).getType()?.let {
|
||||
names += KotlinNameSuggester.suggestNamesByType(it, validator)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
private 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) { 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.effectiveBeanType.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.effectiveBeanType.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 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) }
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.generate
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.PathUtil
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractCodeInsightActionTest
|
||||
import org.jetbrains.kotlin.idea.spring.generate.beanFilter
|
||||
import org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
|
||||
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractGenerateSpringDependencyActionTest : AbstractCodeInsightActionTest() {
|
||||
private var configFile: VirtualFile? = null
|
||||
private var configFilePath: String? = null
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
TestFixtureExtension.loadFixture<SpringTestFixtureExtension>(myModule)
|
||||
}
|
||||
|
||||
override fun configureExtra(mainFilePath: String, mainFileText: String) {
|
||||
configFilePath = InTextDirectivesUtils.findStringWithPrefixes(mainFileText, "// CONFIG_FILE: ")?.let {
|
||||
"${PathUtil.getParentPath(mainFilePath)}/$it"
|
||||
} ?: mainFilePath
|
||||
val springFileSet = TestFixtureExtension
|
||||
.getFixture<SpringTestFixtureExtension>()!!
|
||||
.configureFileSet(myFixture, listOf(configFilePath!!))
|
||||
if (configFilePath != mainFilePath) {
|
||||
configFile = springFileSet.files.single().file!!
|
||||
}
|
||||
|
||||
val beansToChoose = InTextDirectivesUtils.findListWithPrefixes(mainFileText, "// CHOOSE_BEAN: ")
|
||||
project.beanFilter = { it.name in beansToChoose }
|
||||
}
|
||||
|
||||
override fun checkExtra() {
|
||||
configFile?.let {
|
||||
myFixture.openFileInEditor(it)
|
||||
val afterFile = File("$configFilePath.after")
|
||||
TestCase.assertTrue(afterFile.exists())
|
||||
myFixture.checkResult(FileUtil.loadFile(afterFile, true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
configFile = null
|
||||
TestFixtureExtension.unloadFixture<SpringTestFixtureExtension>()
|
||||
super.tearDown()
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.generate;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/spring/core/generate")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class GenerateSpringDependencyActionTestGenerated extends AbstractGenerateSpringDependencyActionTest {
|
||||
public void testAllFilesPresentInGenerate() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/spring/core/generate"), Pattern.compile("^([\\w]+)\\.kt$"));
|
||||
}
|
||||
|
||||
@TestMetadata("beanDependenciesByXml/firstConstructor.kt")
|
||||
public void testBeanDependenciesByXml_FirstConstructor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/spring/core/generate/beanDependenciesByXml/firstConstructor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("beanDependenciesByXml/primaryConstructorAddParam.kt")
|
||||
public void testBeanDependenciesByXml_PrimaryConstructorAddParam() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/spring/core/generate/beanDependenciesByXml/primaryConstructorAddParam.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("beanDependenciesByXml/property.kt")
|
||||
public void testBeanDependenciesByXml_Property() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/spring/core/generate/beanDependenciesByXml/property.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("beanDependenciesByXml/secondaryConstructorAddParam.kt")
|
||||
public void testBeanDependenciesByXml_SecondaryConstructorAddParam() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/spring/core/generate/beanDependenciesByXml/secondaryConstructorAddParam.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("beanDependenciesByXml/setter.kt")
|
||||
public void testBeanDependenciesByXml_Setter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/spring/core/generate/beanDependenciesByXml/setter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.editor
|
||||
|
||||
import com.intellij.codeInsight.template.Template
|
||||
import com.intellij.codeInsight.template.TemplateEditingAdapter
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
import java.util.*
|
||||
|
||||
class BatchTemplateRunner(private val project: Project) {
|
||||
private val elementsAndFactories = ArrayList<Pair<SmartPsiElementPointer<*>, () -> Template?>>()
|
||||
|
||||
fun addTemplateFactory(element: PsiElement, factory: () -> Template?) {
|
||||
elementsAndFactories.add(element.createSmartPointer() to factory)
|
||||
}
|
||||
|
||||
fun runTemplates() {
|
||||
runTemplates(elementsAndFactories.iterator())
|
||||
}
|
||||
|
||||
private fun getEditor(pointer: SmartPsiElementPointer<*>): Editor? {
|
||||
val element = pointer.element ?: return null
|
||||
val virtualFile = element.containingFile?.virtualFile ?: return null
|
||||
val descriptor = OpenFileDescriptor(project, virtualFile, element.textRange.startOffset)
|
||||
return FileEditorManager.getInstance(project).openTextEditor(descriptor, true)
|
||||
}
|
||||
|
||||
private fun runTemplates(iterator: Iterator<Pair<SmartPsiElementPointer<*>, () -> Template?>>) {
|
||||
if (!iterator.hasNext()) return
|
||||
|
||||
val manager = TemplateManager.getInstance(project)
|
||||
project.executeWriteCommand("") {
|
||||
val (pointer, factory) = iterator.next()
|
||||
|
||||
val editor = getEditor(pointer) ?: return@executeWriteCommand
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document)
|
||||
val template = factory() ?: return@executeWriteCommand
|
||||
manager.startTemplate(
|
||||
editor,
|
||||
template,
|
||||
object : TemplateEditingAdapter() {
|
||||
override fun templateFinished(template: Template?, brokenOff: Boolean) {
|
||||
if (brokenOff) return
|
||||
ApplicationManager.getApplication().invokeLater { runTemplates(iterator) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
|
||||
between(ENUM_ENTRY, SEMICOLON).spaces(0)
|
||||
|
||||
beforeInside(FUN, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
beforeInside(SECONDARY_CONSTRUCTOR, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
beforeInside(CLASS, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
beforeInside(OBJECT_DECLARATION, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
before(PROPERTY).lineBreakInCode()
|
||||
|
||||
+3
-1
@@ -64,6 +64,7 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
|
||||
@@ -202,7 +203,8 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
|
||||
Function1<String, Boolean> validator =
|
||||
innerClassBody != null
|
||||
? new NewDeclarationNameValidator(innerClassBody, (PsiElement) null,
|
||||
NewDeclarationNameValidator.Target.VARIABLES)
|
||||
NewDeclarationNameValidator.Target.VARIABLES,
|
||||
Collections.<KtDeclaration>emptyList())
|
||||
: new CollectingNameValidator();
|
||||
List<String> suggestions = KotlinNameSuggester.INSTANCE.suggestNamesByType(getOuterInstanceType(), validator, "outer");
|
||||
parameterField.setSuggestions(ArrayUtil.toStringArray(suggestions));
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean" factory-bean="fooFactoryBean" factory-method="create"/>
|
||||
<bean id="fooFactoryBean" class="a.FooFactoryBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean" factory-bean="fooFactoryBean" factory-method="create">
|
||||
<constructor-arg ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="fooFactoryBean" class="a.FooFactoryBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: factoryMethod-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {<caret>
|
||||
}
|
||||
|
||||
open class FooFactoryBean {
|
||||
open fun create(barBean: BarBean) = FooBean()
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: factoryMethod-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {<caret>
|
||||
}
|
||||
|
||||
open class FooFactoryBean {
|
||||
open fun create() = FooBean()
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean" factory-method="create" />
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean" factory-method="create">
|
||||
<constructor-arg ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: factoryMethod-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {
|
||||
companion object {
|
||||
@JvmStatic fun create(barBean: BarBean): FooBean {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: factoryMethod-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<constructor-arg ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
@@ -0,0 +1,9 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: firstConstructor-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: firstConstructor-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean(barBean: BarBean) {
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<constructor-arg ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
<bean id="bazBean" class="a.BazBean"/>
|
||||
</beans>
|
||||
idea/testData/spring/core/generate/beanDependenciesByXml/primaryConstructorAddParam-config.xml.after
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<constructor-arg ref="barBean"/>
|
||||
<constructor-arg ref="bazBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
<bean id="bazBean" class="a.BazBean"/>
|
||||
</beans>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: primaryConstructorAddParam-config.xml
|
||||
// CHOOSE_BEAN: bazBean
|
||||
package a
|
||||
|
||||
open class FooBean(barBean: BarBean) {<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
|
||||
open class BazBean
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: primaryConstructorAddParam-config.xml
|
||||
// CHOOSE_BEAN: bazBean
|
||||
package a
|
||||
|
||||
open class FooBean(barBean: BarBean, bazBean: BazBean) {
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
|
||||
open class BazBean
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<property name="barBean" ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
@@ -0,0 +1,9 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$LateinitProperty
|
||||
// CONFIG_FILE: property-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
@@ -0,0 +1,10 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$LateinitProperty
|
||||
// CONFIG_FILE: property-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {
|
||||
lateinit var barBean: BarBean
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: secondaryConstructor-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean(n: Int) {
|
||||
<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: secondaryConstructor-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean(n: Int) {<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<constructor-arg ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
<bean id="bazBean" class="a.BazBean"/>
|
||||
</beans>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<constructor-arg ref="barBean"/>
|
||||
<constructor-arg ref="bazBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
<bean id="bazBean" class="a.BazBean"/>
|
||||
</beans>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: secondaryConstructorAddParam-config.xml
|
||||
// CHOOSE_BEAN: bazBean
|
||||
package a
|
||||
|
||||
open class FooBean(n: Int) {
|
||||
constructor(barBean: BarBean): this(1)<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
|
||||
open class BazBean
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Constructor
|
||||
// CONFIG_FILE: secondaryConstructorAddParam-config.xml
|
||||
// CHOOSE_BEAN: bazBean
|
||||
package a
|
||||
|
||||
open class FooBean(n: Int) {
|
||||
constructor(barBean: BarBean, bazBean: BazBean): this(1)
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
|
||||
open class BazBean
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean"/>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
default-init-method="myDefaultInitMethod"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
<bean id="fooBean" class="a.FooBean">
|
||||
<property name="barBean" ref="barBean"/>
|
||||
</bean>
|
||||
<bean id="barBean" class="a.BarBean"/>
|
||||
</beans>
|
||||
@@ -0,0 +1,9 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Setter
|
||||
// CONFIG_FILE: setter-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {<caret>
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
@@ -0,0 +1,11 @@
|
||||
// ACTION_CLASS: org.jetbrains.kotlin.idea.spring.generate.GenerateKotlinSpringBeanDependencyAction$Setter
|
||||
// CONFIG_FILE: setter-config.xml
|
||||
// CHOOSE_BEAN: barBean
|
||||
package a
|
||||
|
||||
open class FooBean {
|
||||
fun setBarBean(barBean: BarBean) {
|
||||
}
|
||||
}
|
||||
|
||||
open class BarBean
|
||||
+11
@@ -37,6 +37,14 @@ abstract class AbstractCodeInsightActionTest : KotlinLightCodeInsightFixtureTest
|
||||
return Class.forName(actionClassName).newInstance() as CodeInsightAction
|
||||
}
|
||||
|
||||
protected open fun configureExtra(mainFilePath: String, mainFileText: String) {
|
||||
|
||||
}
|
||||
|
||||
protected open fun checkExtra() {
|
||||
|
||||
}
|
||||
|
||||
protected open fun testAction(action: AnAction, forced: Boolean): Presentation {
|
||||
val e = TestActionEvent(action)
|
||||
action.beforeActionPerformedUpdate(e)
|
||||
@@ -65,6 +73,7 @@ abstract class AbstractCodeInsightActionTest : KotlinLightCodeInsightFixtureTest
|
||||
.forEach {
|
||||
myFixture.configureByFile(File(rootDir, it).path.replace(File.separator, "/"))
|
||||
}
|
||||
configureExtra(path, fileText)
|
||||
myFixture.configureByFile(path)
|
||||
|
||||
val action = createAction(fileText)
|
||||
@@ -83,6 +92,8 @@ abstract class AbstractCodeInsightActionTest : KotlinLightCodeInsightFixtureTest
|
||||
val afterFile = File("$path.after")
|
||||
TestCase.assertTrue(afterFile.exists())
|
||||
myFixture.checkResult(FileUtil.loadFile(afterFile, true))
|
||||
|
||||
checkExtra()
|
||||
}
|
||||
}
|
||||
catch (e: CommonRefactoringUtil.RefactoringErrorHintException) {
|
||||
|
||||
Reference in New Issue
Block a user