-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathScenarioReferenceResolver.kt
125 lines (100 loc) · 5.17 KB
/
ScenarioReferenceResolver.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package com.justai.jaicf.plugin.scenarios.linker
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiModificationTracker
import com.justai.jaicf.plugin.scenarios.JaicfService
import com.justai.jaicf.plugin.scenarios.psi.ScenarioDataService
import com.justai.jaicf.plugin.scenarios.psi.builders.isStateDeclaration
import com.justai.jaicf.plugin.scenarios.psi.dto.Append
import com.justai.jaicf.plugin.scenarios.psi.dto.Scenario
import com.justai.jaicf.plugin.scenarios.psi.dto.State
import com.justai.jaicf.plugin.scenarios.psi.dto.TopLevelAppend
import com.justai.jaicf.plugin.utils.SCENARIO_MODEL_FIELD_NAME
import com.justai.jaicf.plugin.utils.argumentExpressionOrDefaultValue
import com.justai.jaicf.plugin.utils.isExist
import com.justai.jaicf.plugin.utils.isRemoved
import com.justai.jaicf.plugin.utils.safeResolve
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclarationContainer
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
class ScenarioReferenceResolver(project: Project) : JaicfService(project) {
private val scenarioService = ScenarioDataService.getInstance(project)
private val resolvedReferences by cached(PsiModificationTracker.MODIFICATION_COUNT) {
mutableMapOf<Pair<KtReferenceExpression, State?>, Scenario?>()
}
fun resolve(scenarioReference: KtReferenceExpression, boundedState: State? = null): Scenario? {
if (scenarioReference.isRemoved || !enabled) return null
resolvedReferences?.get(scenarioReference to boundedState)?.let { return it }
val body = getScenarioBody(scenarioReference, boundedState) ?: return null
return resolveScenario(body)?.also {
resolvedReferences?.set(scenarioReference to boundedState, it)
}
}
private fun resolveScenario(scenarioBody: KtExpression): Scenario? {
val file = scenarioBody.containingFile as? KtFile ?: return null
return scenarioService.getScenarios(file)?.findBoundingScenario(scenarioBody)
}
private fun getScenarioBody(scenarioReference: KtReferenceExpression, boundedState: State? = null): KtExpression? {
when (val resolvedElement = scenarioReference.safeResolve()) {
is KtObjectDeclaration -> {
return resolvedElement.scenarioBody
}
is KtProperty -> {
return resolvedElement.delegateExpressionOrInitializer ?: resolvedElement.getter?.initializer
}
is KtParameter -> {
(resolvedElement.defaultValue as? KtReferenceExpression)?.let {
return getScenarioBody(it)
}
val parameterName = resolvedElement.name ?: return null
val argumentExpression =
boundedState?.stateExpression?.argumentExpressionOrDefaultValue(parameterName) as? KtReferenceExpression
return argumentExpression?.let { getScenarioBody(it) }
}
is KtCallExpression -> {
return if (resolvedElement.isStateDeclaration) scenarioReference else null
}
else -> {
return (scenarioReference as? KtCallExpression)?.let(::getScenarioBody)
}
}
}
private fun getScenarioBody(callExpression: KtCallExpression): KtExpression? {
if (callExpression.isStateDeclaration) return callExpression
return when (val resolved = callExpression.referenceExpression()?.safeResolve()) {
is KtNamedFunction -> (resolved.initializer as? KtCallExpression)?.let {
if (it.isStateDeclaration) it else null
}
is KtClass -> resolved.body?.scenarioBody
is KtPrimaryConstructor ->
callExpression.argumentExpressionOrDefaultValue(SCENARIO_MODEL_FIELD_NAME)
else -> null
}
}
private val KtDeclarationContainer.scenarioBody: KtExpression?
get() = declarations
.filter { it.name == SCENARIO_MODEL_FIELD_NAME && it is KtProperty }
.map { it as KtProperty }
.mapNotNull { it.delegateExpressionOrInitializer ?: it.getter?.initializer }
.firstOrNull()
companion object {
fun getInstance(element: PsiElement): ScenarioReferenceResolver? =
if (element.isExist) getInstance(element.project)
else null
fun getInstance(project: Project): ScenarioReferenceResolver =
project.getService(ScenarioReferenceResolver::class.java)
}
}
val Append.scenario
get() = ScenarioReferenceResolver.getInstance(project).resolve(this.referenceToScenario, parentState)
val TopLevelAppend.scenario
get() = this.referenceToScenario?.let { ScenarioReferenceResolver.getInstance(project).resolve(it) }