Skip to content

Commit d5333be

Browse files
committed
Converting optional types to corresponding types
1 parent 2dcabe8 commit d5333be

File tree

8 files changed

+91
-30
lines changed

8 files changed

+91
-30
lines changed

src/main/kotlin/org/domaframework/doma/intellij/common/sql/PsiClassTypeUtil.kt

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,46 @@ class PsiClassTypeUtil {
6363
type = type.parameters.firstOrNull()
6464
count++
6565
}
66-
return type as? PsiClassType
66+
val convertOptional = type?.let { convertOptionalType(it, project) }
67+
return convertOptional as? PsiClassType
6768
}
6869
return null
6970
}
71+
72+
/**
73+
* Check if daoParamType is an instance of PsiClassType representing Optional or its primitive variants
74+
*/
75+
fun convertOptionalType(
76+
daoParamType: PsiType,
77+
project: Project,
78+
): PsiType {
79+
if (daoParamType is PsiClassType) {
80+
val resolved = daoParamType.resolve()
81+
val optionalTypeMap =
82+
mapOf(
83+
"java.util.OptionalInt" to "java.lang.Integer",
84+
"java.util.OptionalDouble" to "java.lang.Double",
85+
"java.util.OptionalLong" to "java.lang.Long",
86+
)
87+
if (resolved != null) {
88+
when (resolved.qualifiedName) {
89+
"java.util.Optional" -> return daoParamType.parameters.firstOrNull()
90+
?: daoParamType
91+
92+
else ->
93+
optionalTypeMap[resolved.qualifiedName]?.let { optionalType ->
94+
val newType =
95+
PsiType.getTypeByName(
96+
optionalType,
97+
project,
98+
GlobalSearchScope.allScope(project),
99+
)
100+
return newType
101+
}
102+
}
103+
}
104+
}
105+
return daoParamType
106+
}
70107
}
71108
}

src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class ForDirectiveUtil {
259259

260260
val matchParam = daoMethod.findParameter(cleanString(topElementText))
261261
val daoParamType = matchParam?.type ?: return null
262-
fieldAccessTopParentClass = PsiParentClass(daoParamType)
262+
fieldAccessTopParentClass = PsiParentClass(PsiClassTypeUtil.convertOptionalType(daoParamType, project))
263263
}
264264
fieldAccessTopParentClass?.let {
265265
getFieldAccessLastPropertyClassType(
@@ -329,14 +329,15 @@ class ForDirectiveUtil {
329329
): ValidationResult? {
330330
var parent =
331331
if (isBatchAnnotation) {
332-
val parentType = topParent.type
332+
val parentType = PsiClassTypeUtil.convertOptionalType(topParent.type, project)
333333
val nextClassType = parentType as? PsiClassType ?: return null
334334
val nestType = nextClassType.parameters.firstOrNull() ?: return null
335-
PsiParentClass(nestType)
335+
PsiParentClass(PsiClassTypeUtil.convertOptionalType(nestType, project))
336336
} else {
337-
topParent
337+
val convertOptional = PsiClassTypeUtil.convertOptionalType(topParent.type, project)
338+
PsiParentClass(convertOptional)
338339
}
339-
val parentType = parent.type
340+
val parentType = PsiClassTypeUtil.convertOptionalType(parent.type, project)
340341
val classType = parentType as? PsiClassType ?: return null
341342

342343
var competeResult: ValidationCompleteResult? = null
@@ -353,8 +354,8 @@ class ForDirectiveUtil {
353354
// When a List type element is used as the parent,
354355
// the original declared type is retained and the referenced type is obtained by nesting.
355356
var parentListBaseType: PsiType? =
356-
if (PsiClassTypeUtil.Companion.isIterableType(classType, project)) {
357-
parentType
357+
if (PsiClassTypeUtil.isIterableType(classType, project)) {
358+
PsiClassTypeUtil.convertOptionalType(parentType, project)
358359
} else {
359360
null
360361
}
@@ -375,24 +376,25 @@ class ForDirectiveUtil {
375376
parent
376377
.findField(searchElm)
377378
?.let { match ->
379+
val convertOptional = PsiClassTypeUtil.convertOptionalType(match.type, project)
378380
val type =
379381
parentListBaseType?.let {
380-
PsiClassTypeUtil.Companion.getParameterType(
382+
PsiClassTypeUtil.getParameterType(
381383
project,
382-
match.type,
384+
convertOptional,
383385
it,
384386
nestIndex,
385387
)
386388
}
387-
?: match.type
389+
?: convertOptional
388390
val classType = type as? PsiClassType
389391
if (classType != null &&
390-
PsiClassTypeUtil.Companion.isIterableType(
392+
PsiClassTypeUtil.isIterableType(
391393
classType,
392394
element.project,
393395
)
394396
) {
395-
parentListBaseType = type
397+
parentListBaseType = PsiClassTypeUtil.convertOptionalType(type, project)
396398
nestIndex = 0
397399
}
398400
findFieldMethod?.invoke(type)
@@ -402,19 +404,20 @@ class ForDirectiveUtil {
402404
.findMethod(searchElm)
403405
?.let { match ->
404406
val returnType = match.returnType ?: return null
407+
val convertOptionalType = PsiClassTypeUtil.convertOptionalType(returnType, project)
405408
val methodReturnType =
406409
parentListBaseType?.let {
407-
PsiClassTypeUtil.Companion.getParameterType(
410+
PsiClassTypeUtil.getParameterType(
408411
project,
409-
returnType,
412+
convertOptionalType,
410413
it,
411414
nestIndex,
412415
)
413416
}
414-
?: returnType
417+
?: convertOptionalType
415418
val classType = methodReturnType as? PsiClassType
416419
if (classType != null &&
417-
PsiClassTypeUtil.Companion.isIterableType(
420+
PsiClassTypeUtil.isIterableType(
418421
classType,
419422
element.project,
420423
)

src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import com.intellij.util.ProcessingContext
3535
import org.domaframework.doma.intellij.common.dao.findDaoMethod
3636
import org.domaframework.doma.intellij.common.psi.PsiParentClass
3737
import org.domaframework.doma.intellij.common.psi.PsiPatternUtil
38+
import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil
3839
import org.domaframework.doma.intellij.common.sql.cleanString
3940
import org.domaframework.doma.intellij.common.sql.directive.DirectiveCompletion
4041
import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult
@@ -385,7 +386,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
385386
return null
386387
}
387388
val immediate = findParam.getIterableClazz(daoMethod.getDomaAnnotationType())
388-
return immediate.type
389+
return PsiClassTypeUtil.convertOptionalType(immediate.type, originalFile.project)
389390
}
390391

391392
private fun getRefClazz(

src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr
2828

2929
class DocumentDaoParameterGenerator(
3030
val originalElement: PsiElement,
31-
val project: Project,
31+
override val project: Project,
3232
val result: MutableList<String?>,
33-
) : DocumentGenerator() {
33+
) : DocumentGenerator(project) {
3434
override fun generateDocument() {
3535
var topParentType: PsiParentClass? = null
3636
val selfSkip = isSelfSkip(originalElement)

src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentGenerator.kt

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@
1515
*/
1616
package org.domaframework.doma.intellij.document.generator
1717

18+
import com.intellij.openapi.project.Project
1819
import com.intellij.psi.PsiElement
1920
import org.domaframework.doma.intellij.common.psi.PsiParentClass
21+
import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil
2022
import org.domaframework.doma.intellij.common.sql.foritem.ForItem
2123
import org.domaframework.doma.intellij.extension.psi.getForItem
2224

23-
abstract class DocumentGenerator {
25+
abstract class DocumentGenerator(
26+
open val project: Project,
27+
) {
2428
abstract fun generateDocument()
2529

2630
protected fun isSelfSkip(targetElement: PsiElement): Boolean {
@@ -30,8 +34,10 @@ abstract class DocumentGenerator {
3034
}
3135

3236
protected fun generateTypeLink(parentClass: PsiParentClass?): String {
33-
if (parentClass?.type != null) {
34-
return generateTypeLinkFromCanonicalText(parentClass.type.canonicalText)
37+
val parentClassType = parentClass?.type
38+
if (parentClassType != null) {
39+
val convertOptionalType = PsiClassTypeUtil.convertOptionalType(parentClassType, project)
40+
return generateTypeLinkFromCanonicalText(convertOptionalType.canonicalText)
3541
}
3642
return ""
3743
}
@@ -40,22 +46,34 @@ abstract class DocumentGenerator {
4046
val regex = Regex("([a-zA-Z0-9_]+\\.)*([a-zA-Z0-9_]+)")
4147
val result = StringBuilder()
4248
var lastIndex = 0
49+
val optionalPackage = "java.util.Optional"
50+
val optionalTypeMap =
51+
listOf(
52+
optionalPackage,
53+
"${optionalPackage}Int",
54+
"${optionalPackage}Double",
55+
"${optionalPackage}Long",
56+
)
57+
var skipCount = 0
4358

4459
for (match in regex.findAll(canonicalText)) {
4560
val fullMatch = match.value
61+
val optionalSkip = optionalTypeMap.contains(fullMatch)
62+
if (optionalSkip) skipCount++
63+
4664
val typeName = match.groups[2]?.value ?: fullMatch
4765
val startIndex = match.range.first
4866
val endIndex = match.range.last + 1
4967

50-
if (lastIndex < startIndex) {
68+
if (lastIndex < startIndex && !optionalSkip) {
5169
result.append(canonicalText.substring(lastIndex, startIndex))
5270
}
53-
result.append("<a href=\"psi_element://$fullMatch\">$typeName</a>")
71+
if (!optionalSkip) result.append("<a href=\"psi_element://$fullMatch\">$typeName</a>")
5472
lastIndex = endIndex
5573
}
5674

57-
if (lastIndex < canonicalText.length) {
58-
result.append(canonicalText.substring(lastIndex))
75+
if (lastIndex + skipCount < canonicalText.length) {
76+
result.append(canonicalText.substring(lastIndex + skipCount))
5977
}
6078

6179
return result.toString()

src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentStaticFieldGenerator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr
2929

3030
class DocumentStaticFieldGenerator(
3131
val originalElement: PsiElement,
32-
val project: Project,
32+
override val project: Project,
3333
val result: MutableList<String?>,
3434
val staticFieldAccessExpr: SqlElStaticFieldAccessExpr,
3535
val file: PsiFile,
36-
) : DocumentGenerator() {
36+
) : DocumentGenerator(project) {
3737
override fun generateDocument() {
3838
val fieldAccessBlocks = staticFieldAccessExpr.accessElements
3939
val staticElement = PsiStaticElement(fieldAccessBlocks, file)

src/main/kotlin/org/domaframework/doma/intellij/extension/psi/PsiParameterExtension.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ fun PsiParameter.getIterableClazz(useListParam: Boolean): PsiParentClass {
3030
val immediate = this.type as? PsiClassType
3131
val classType = immediate?.let { PsiClassTypeUtil.getPsiTypeByList(it, this.project, useListParam) }
3232
if (classType != null) {
33-
return PsiParentClass(classType)
33+
val convertOptional = PsiClassTypeUtil.convertOptionalType(classType, this.project)
34+
return PsiParentClass(convertOptional)
3435
}
3536
return PsiParentClass(this.type)
3637
}

src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class SqlInspectionVisitor(
132132
if (forItem != null) {
133133
val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks)
134134
if (result == null) {
135+
// TODO Add an error message when the type of element used in the For directory is not a List type.
135136
errorHighlight(topElement, daoMethod, holder)
136137
return
137138
}

0 commit comments

Comments
 (0)