diff --git a/src/main/kotlin/org/axonframework/intellij/ide/plugin/resolving/AggregateStructureResolver.kt b/src/main/kotlin/org/axonframework/intellij/ide/plugin/resolving/AggregateStructureResolver.kt index 50c4980b..83a29142 100644 --- a/src/main/kotlin/org/axonframework/intellij/ide/plugin/resolving/AggregateStructureResolver.kt +++ b/src/main/kotlin/org/axonframework/intellij/ide/plugin/resolving/AggregateStructureResolver.kt @@ -97,13 +97,18 @@ class AggregateStructureResolver(private val project: Project) { .flatMap { AnnotatedElementsSearch.searchPsiClasses(it.psiClass, project.axonScope()).findAll() } - .mapNotNull { inspect(it, null) } + .mapNotNull { inspect(it, emptyList()) } .flatMap { it.flatten() } - private fun inspect(clazz: PsiClass, parent: PsiClass?): Entity? { + private fun inspect(clazz: PsiClass, parents: List, depth: Int = 0): Entity? { if (clazz.isEnum) { return null } + if(parents.contains(clazz) || depth > 20) { + // Guard for infinite recursion; we already have this class indexed, or we exceed an exorbitant depth + return null + } + val parent = parents.lastOrNull() val children = clazz.fields.toList() .filter { it.isAnnotated(AxonAnnotation.AGGREGATE_MEMBER) } .mapNotNull { field -> @@ -112,7 +117,7 @@ class AggregateStructureResolver(private val project: Project) { val qualifiedName = psiType.toQualifiedName() ?: return@mapNotNull null val targetClass = clazz.javaFacade().findClass(qualifiedName, clazz.project.axonScope()) ?: return@mapNotNull null - val modelMember = inspect(targetClass, clazz) ?: return@mapNotNull null + val modelMember = inspect(targetClass, parents + clazz, depth + 1) ?: return@mapNotNull null val routingKey = field.resolveAnnotationStringValue(AxonAnnotation.AGGREGATE_MEMBER, "routingKey") val eventForwardingMode = field.resolveAnnotationClassValue(AxonAnnotation.AGGREGATE_MEMBER, "eventForwardingMode") EntityMember(field, field.name, modelMember, isCollection, routingKey, eventForwardingMode) diff --git a/src/test/kotlin/org/axonframework/intellij/ide/plugin/markers/ClassLineMarkerProviderTest.kt b/src/test/kotlin/org/axonframework/intellij/ide/plugin/markers/ClassLineMarkerProviderTest.kt index a4330168..831dd85b 100644 --- a/src/test/kotlin/org/axonframework/intellij/ide/plugin/markers/ClassLineMarkerProviderTest.kt +++ b/src/test/kotlin/org/axonframework/intellij/ide/plugin/markers/ClassLineMarkerProviderTest.kt @@ -102,6 +102,28 @@ class ClassLineMarkerProviderTest : AbstractAxonFixtureTestCase() { ) } + fun `test shows line marker on aggregate of hierarchy when recursive`() { + addFile( + "MyAggregate.kt", """ + class RecursiveAggregateMember { + @AggregateMember + private var subItems: List + } + + @AggregateRoot + class MyAggregate { + @AggregateMember + private lateinit var singleMember: RecursiveAggregateMember + } + """.trimIndent(), open = true + ) + Assertions.assertThat(hasLineMarker(ClassLineMarkerProvider::class.java)).isTrue + Assertions.assertThat(getLineMarkerOptions(ClassLineMarkerProvider::class.java)).containsExactly( + OptionSummary("MyAggregate", null, AxonIcons.Axon), + OptionSummary("- RecursiveAggregateMember", null, AxonIcons.Axon), + ) + } + fun `test shows line marker on child of hierarchy`() { addFile( "MyAggregate.kt", """