Skip to content

Commit bdb8f2c

Browse files
authored
Merge pull request #257 from domaframework/feature/check-dao-method-params
DAO Method Parameter Validation
2 parents a95684c + f5d1be1 commit bdb8f2c

File tree

71 files changed

+2497
-110
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2497
-110
lines changed

src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiTypeChecker.kt

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,47 +20,51 @@ import com.intellij.psi.PsiClassType
2020
import com.intellij.psi.PsiPrimitiveType
2121
import com.intellij.psi.PsiType
2222
import com.intellij.psi.util.PsiUtil
23+
import org.domaframework.doma.intellij.common.util.DomaClassName
2324

2425
object PsiTypeChecker {
2526
private val TARGET_CLASSES: MutableSet<String> = HashSet()
2627
private val WRAPPER_CLASSES: MutableSet<String> = HashSet()
2728

2829
init {
29-
TARGET_CLASSES.add("java.lang.String")
30-
TARGET_CLASSES.add("java.lang.Object")
31-
TARGET_CLASSES.add("java.math.BigDecimal")
32-
TARGET_CLASSES.add("java.math.BigInteger")
33-
TARGET_CLASSES.add("java.time.LocalDate")
34-
TARGET_CLASSES.add("java.time.LocalTime")
35-
TARGET_CLASSES.add("java.time.LocalDateTime")
36-
TARGET_CLASSES.add("java.sql.Date")
37-
TARGET_CLASSES.add("java.sql.Time")
38-
TARGET_CLASSES.add("java.sql.Timestamp")
39-
TARGET_CLASSES.add("java.sql.Array")
40-
TARGET_CLASSES.add("java.sql.Blob")
41-
TARGET_CLASSES.add("java.sql.Clob")
42-
TARGET_CLASSES.add("java.sql.SQLXML")
43-
TARGET_CLASSES.add("java.util.Date")
30+
TARGET_CLASSES.add(DomaClassName.STRING.className)
31+
TARGET_CLASSES.add(DomaClassName.OBJECT.className)
32+
TARGET_CLASSES.add(DomaClassName.BIG_DECIMAL.className)
33+
TARGET_CLASSES.add(DomaClassName.BIG_INTEGER.className)
34+
TARGET_CLASSES.add(DomaClassName.LOCAL_DATE.className)
35+
TARGET_CLASSES.add(DomaClassName.LOCAL_TIME.className)
36+
TARGET_CLASSES.add(DomaClassName.LOCAL_DATE_TIME.className)
37+
TARGET_CLASSES.add(DomaClassName.SQL_DATE.className)
38+
TARGET_CLASSES.add(DomaClassName.SQL_TIME.className)
39+
TARGET_CLASSES.add(DomaClassName.SQL_TIMESTAMP.className)
40+
TARGET_CLASSES.add(DomaClassName.SQL_ARRAY.className)
41+
TARGET_CLASSES.add(DomaClassName.SQL_BLOB.className)
42+
TARGET_CLASSES.add(DomaClassName.SQL_CLOB.className)
43+
TARGET_CLASSES.add(DomaClassName.SQL_XML.className)
44+
TARGET_CLASSES.add(DomaClassName.UTIL_DATE.className)
4445

45-
WRAPPER_CLASSES.add("java.lang.Byte")
46-
WRAPPER_CLASSES.add("java.lang.Short")
47-
WRAPPER_CLASSES.add("java.lang.Integer")
48-
WRAPPER_CLASSES.add("java.lang.Long")
49-
WRAPPER_CLASSES.add("java.lang.Float")
50-
WRAPPER_CLASSES.add("java.lang.Double")
51-
WRAPPER_CLASSES.add("java.lang.Boolean")
46+
WRAPPER_CLASSES.add(DomaClassName.BYTE.className)
47+
WRAPPER_CLASSES.add(DomaClassName.SHORT.className)
48+
WRAPPER_CLASSES.add(DomaClassName.INTEGER.className)
49+
WRAPPER_CLASSES.add(DomaClassName.LONG.className)
50+
WRAPPER_CLASSES.add(DomaClassName.FLOAT.className)
51+
WRAPPER_CLASSES.add(DomaClassName.DOUBLE.className)
52+
WRAPPER_CLASSES.add(DomaClassName.BOOLEAN.className)
5253
}
5354

5455
/**
5556
* Determines whether the specified PsiType satisfies the conditions.
5657
* @param psiType Check target PsiType
5758
* @return true if the condition is met
5859
*/
59-
fun isTargetType(psiType: PsiType?): Boolean {
60+
fun isBaseClassType(psiType: PsiType?): Boolean {
6061
if (psiType == null) return false
62+
// Check if the type is a primitive type
6163
if (psiType is PsiPrimitiveType && psiType.canonicalText == "char") {
6264
return false
6365
}
66+
67+
// Check if the type is a wrapper class
6468
if (psiType is PsiClassType) {
6569
val psiClass = PsiUtil.resolveClassInType(psiType)
6670
if (psiClass != null) {
@@ -79,10 +83,9 @@ object PsiTypeChecker {
7983
}
8084
if (psiType is PsiArrayType) {
8185
val componentType = psiType.componentType.canonicalText
82-
if ("java.lang.Byte" == componentType) {
83-
return true
84-
}
86+
return DomaClassName.BYTE.className == componentType
8587
}
86-
return true
88+
// TODO If the condition does not match, return false to strengthen type checking.
89+
return false
8790
}
8891
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.intellij.openapi.project.Project
1919
import com.intellij.psi.PsiClassType
2020
import com.intellij.psi.PsiType
2121
import com.intellij.psi.search.GlobalSearchScope
22+
import org.domaframework.doma.intellij.common.util.DomaClassName
2223

2324
class PsiClassTypeUtil {
2425
companion object {
@@ -28,7 +29,7 @@ class PsiClassTypeUtil {
2829
): Boolean {
2930
val iterableType =
3031
PsiType.getTypeByName(
31-
"java.lang.Iterable",
32+
DomaClassName.ITERABLE.className,
3233
project,
3334
GlobalSearchScope.allScope(project),
3435
)
@@ -68,15 +69,15 @@ class PsiClassTypeUtil {
6869
val resolved = daoParamType.resolve()
6970
val optionalTypeMap =
7071
mapOf(
71-
"java.util.OptionalInt" to "java.lang.Integer",
72-
"java.util.OptionalDouble" to "java.lang.Double",
73-
"java.util.OptionalLong" to "java.lang.Long",
72+
DomaClassName.OPTIONAL_INT.className to DomaClassName.INTEGER.className,
73+
DomaClassName.OPTIONAL_DOUBLE.className to DomaClassName.DOUBLE.className,
74+
DomaClassName.OPTIONAL_LONG.className to DomaClassName.LONG.className,
7475
)
7576
if (resolved != null) {
7677
when (resolved.qualifiedName) {
7778
// If the type is java.util.Optional, return its parameter type if available;
7879
// otherwise, return the original daoParamType.
79-
"java.util.Optional" -> return daoParamType.parameters.firstOrNull()
80+
DomaClassName.OPTIONAL.className -> return daoParamType.parameters.firstOrNull()
8081
?: daoParamType
8182

8283
// For primitive Optional types (e.g., OptionalInt, OptionalDouble),

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import com.intellij.psi.PsiElement
2121
import com.intellij.psi.PsiFile
2222
import com.intellij.psi.PsiMethod
2323
import org.domaframework.doma.intellij.common.dao.findDaoMethod
24-
import org.domaframework.doma.intellij.common.psi.PsiTypeChecker
2524
import org.domaframework.doma.intellij.extension.psi.searchParameter
2625

2726
class LiteralDirectiveHandler(
@@ -39,9 +38,7 @@ class LiteralDirectiveHandler(
3938
) { daoMethod, bind ->
4039
daoMethod
4140
?.searchParameter(bind)
42-
?.filter {
43-
PsiTypeChecker.isTargetType(it.type)
44-
}?.map { param -> VariableLookupItem(param) }
41+
?.map { param -> VariableLookupItem(param) }
4542
?.toList()
4643
?: emptyList()
4744
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.util
17+
18+
enum class DomaClassName(
19+
val className: String,
20+
) {
21+
OPTIONAL("java.util.Optional"),
22+
OPTIONAL_INT("java.util.OptionalInt"),
23+
OPTIONAL_DOUBLE("java.util.OptionalDouble"),
24+
OPTIONAL_LONG("java.util.OptionalLong"),
25+
26+
INTEGER("java.lang.Integer"),
27+
DOUBLE("java.lang.Double"),
28+
LONG("java.lang.Long"),
29+
30+
MAP("java.util.Map"),
31+
LIST("java.util.List"),
32+
ITERABLE("java.lang.Iterable"),
33+
34+
DOMAIN("org.seasar.doma.Domain"),
35+
BI_FUNCTION("java.util.function.BiFunction"),
36+
CONFIG("org.seasar.doma.jdbc.Config"),
37+
PREPARED_SQL("org.seasar.doma.jdbc.PreparedSql"),
38+
VOID("java.lang.Void"),
39+
RETURNING("org.seasar.doma.Returning"),
40+
REFERENCE("org.seasar.doma.jdbc.Reference"),
41+
42+
STRING("java.lang.String"),
43+
OBJECT("java.lang.Object"),
44+
BIG_DECIMAL("java.math.BigDecimal"),
45+
BIG_INTEGER("java.math.BigInteger"),
46+
LOCAL_DATE("java.time.LocalDate"),
47+
LOCAL_TIME("java.time.LocalTime"),
48+
LOCAL_DATE_TIME("java.time.LocalDateTime"),
49+
SQL_DATE("java.sql.Date"),
50+
SQL_TIME("java.sql.Time"),
51+
SQL_TIMESTAMP("java.sql.Timestamp"),
52+
SQL_ARRAY("java.sql.Array"),
53+
SQL_BLOB("java.sql.Blob"),
54+
SQL_CLOB("java.sql.Clob"),
55+
SQL_XML("java.sql.SQLXML"),
56+
UTIL_DATE("java.util.Date"),
57+
58+
BYTE("java.lang.Byte"),
59+
SHORT("java.lang.Short"),
60+
FLOAT("java.lang.Float"),
61+
BOOLEAN("java.lang.Boolean"),
62+
63+
JAVA_FUNCTION("java.util.function.Function"),
64+
JAVA_COLLECTOR("java.util.stream.Collector"),
65+
JAVA_STREAM("java.util.stream.Stream"),
66+
SELECT_TYPE("org.seasar.doma.SelectType"),
67+
68+
ENTITY("org.seasar.doma.Entity"),
69+
;
70+
71+
fun isTargetClassNameStartsWith(paramTypeCanonicalNames: String): Boolean = paramTypeCanonicalNames.startsWith(this.className)
72+
73+
fun getGenericParamCanonicalText(vararg genericParas: String): String = "${this.className}<${genericParas.joinToString(", ")}>"
74+
75+
companion object {
76+
fun isOptionalWrapperType(paramTypeCanonicalName: String): Boolean =
77+
paramTypeCanonicalName in
78+
listOf(
79+
OPTIONAL_INT.className,
80+
OPTIONAL_DOUBLE.className,
81+
OPTIONAL_LONG.className,
82+
)
83+
}
84+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.validation.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.util.TextRange
20+
import com.intellij.psi.PsiElement
21+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
22+
import org.domaframework.doma.intellij.common.util.DomaClassName
23+
24+
class ValidationMethodBiFunctionParamResult(
25+
override val identify: PsiElement?,
26+
override val shortName: String = "",
27+
private val index: Int,
28+
) : ValidationResult(identify, null, shortName) {
29+
override fun setHighlight(
30+
highlightRange: TextRange,
31+
identify: PsiElement,
32+
holder: ProblemsHolder,
33+
parent: PsiParentClass?,
34+
) {
35+
val project = identify.project
36+
holder.registerProblem(
37+
identify,
38+
getCheckParamIndexMessage(index),
39+
problemHighlightType(project, shortName),
40+
highlightRange,
41+
)
42+
}
43+
44+
private fun getCheckParamIndexMessage(index: Int): String =
45+
when (index) {
46+
0 -> "The first type argument of BiFunction must be ${DomaClassName.CONFIG.className}"
47+
1 -> "The second type argument of BiFunction must be ${DomaClassName.PREPARED_SQL.className}"
48+
49+
else -> "The type argument at index $index is not supported"
50+
}
51+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.validation.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.util.TextRange
20+
import com.intellij.psi.PsiElement
21+
import org.domaframework.doma.intellij.bundle.MessageBundle
22+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
23+
import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType
24+
25+
class ValidationMethodHasRequireClassParamResult(
26+
override val identify: PsiElement?,
27+
override val shortName: String = "",
28+
private val annotationType: DomaAnnotationType,
29+
private val requiredClass: String,
30+
) : ValidationResult(identify, null, shortName) {
31+
override fun setHighlight(
32+
highlightRange: TextRange,
33+
identify: PsiElement,
34+
holder: ProblemsHolder,
35+
parent: PsiParentClass?,
36+
) {
37+
val project = identify.project
38+
holder.registerProblem(
39+
identify,
40+
MessageBundle.message("inspection.invalid.dao.params.require.type", annotationType.name, requiredClass),
41+
problemHighlightType(project, shortName),
42+
highlightRange,
43+
)
44+
}
45+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.validation.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.util.TextRange
20+
import com.intellij.psi.PsiElement
21+
import org.domaframework.doma.intellij.bundle.MessageBundle
22+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
23+
24+
class ValidationMethodNotSelectStreamParamResult(
25+
override val identify: PsiElement?,
26+
override val shortName: String = "",
27+
) : ValidationResult(identify, null, shortName) {
28+
override fun setHighlight(
29+
highlightRange: TextRange,
30+
identify: PsiElement,
31+
holder: ProblemsHolder,
32+
parent: PsiParentClass?,
33+
) {
34+
val project = identify.project
35+
holder.registerProblem(
36+
identify,
37+
MessageBundle.message("inspection.invalid.dao.select.param.notFound.strategy.stream"),
38+
problemHighlightType(project, shortName),
39+
highlightRange,
40+
)
41+
}
42+
}

0 commit comments

Comments
 (0)