Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,51 @@ import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiPrimitiveType
import com.intellij.psi.PsiType
import com.intellij.psi.util.PsiUtil
import org.domaframework.doma.intellij.common.util.DomaClassName

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

init {
TARGET_CLASSES.add("java.lang.String")
TARGET_CLASSES.add("java.lang.Object")
TARGET_CLASSES.add("java.math.BigDecimal")
TARGET_CLASSES.add("java.math.BigInteger")
TARGET_CLASSES.add("java.time.LocalDate")
TARGET_CLASSES.add("java.time.LocalTime")
TARGET_CLASSES.add("java.time.LocalDateTime")
TARGET_CLASSES.add("java.sql.Date")
TARGET_CLASSES.add("java.sql.Time")
TARGET_CLASSES.add("java.sql.Timestamp")
TARGET_CLASSES.add("java.sql.Array")
TARGET_CLASSES.add("java.sql.Blob")
TARGET_CLASSES.add("java.sql.Clob")
TARGET_CLASSES.add("java.sql.SQLXML")
TARGET_CLASSES.add("java.util.Date")
TARGET_CLASSES.add(DomaClassName.STRING.className)
TARGET_CLASSES.add(DomaClassName.OBJECT.className)
TARGET_CLASSES.add(DomaClassName.BIG_DECIMAL.className)
TARGET_CLASSES.add(DomaClassName.BIG_INTEGER.className)
TARGET_CLASSES.add(DomaClassName.LOCAL_DATE.className)
TARGET_CLASSES.add(DomaClassName.LOCAL_TIME.className)
TARGET_CLASSES.add(DomaClassName.LOCAL_DATE_TIME.className)
TARGET_CLASSES.add(DomaClassName.SQL_DATE.className)
TARGET_CLASSES.add(DomaClassName.SQL_TIME.className)
TARGET_CLASSES.add(DomaClassName.SQL_TIMESTAMP.className)
TARGET_CLASSES.add(DomaClassName.SQL_ARRAY.className)
TARGET_CLASSES.add(DomaClassName.SQL_BLOB.className)
TARGET_CLASSES.add(DomaClassName.SQL_CLOB.className)
TARGET_CLASSES.add(DomaClassName.SQL_XML.className)
TARGET_CLASSES.add(DomaClassName.UTIL_DATE.className)

WRAPPER_CLASSES.add("java.lang.Byte")
WRAPPER_CLASSES.add("java.lang.Short")
WRAPPER_CLASSES.add("java.lang.Integer")
WRAPPER_CLASSES.add("java.lang.Long")
WRAPPER_CLASSES.add("java.lang.Float")
WRAPPER_CLASSES.add("java.lang.Double")
WRAPPER_CLASSES.add("java.lang.Boolean")
WRAPPER_CLASSES.add(DomaClassName.BYTE.className)
WRAPPER_CLASSES.add(DomaClassName.SHORT.className)
WRAPPER_CLASSES.add(DomaClassName.INTEGER.className)
WRAPPER_CLASSES.add(DomaClassName.LONG.className)
WRAPPER_CLASSES.add(DomaClassName.FLOAT.className)
WRAPPER_CLASSES.add(DomaClassName.DOUBLE.className)
WRAPPER_CLASSES.add(DomaClassName.BOOLEAN.className)
}

/**
* Determines whether the specified PsiType satisfies the conditions.
* @param psiType Check target PsiType
* @return true if the condition is met
*/
fun isTargetType(psiType: PsiType?): Boolean {
fun isBaseClassType(psiType: PsiType?): Boolean {
if (psiType == null) return false
// Check if the type is a primitive type
if (psiType is PsiPrimitiveType && psiType.canonicalText == "char") {
return false
}

// Check if the type is a wrapper class
if (psiType is PsiClassType) {
val psiClass = PsiUtil.resolveClassInType(psiType)
if (psiClass != null) {
Expand All @@ -79,10 +83,9 @@ object PsiTypeChecker {
}
if (psiType is PsiArrayType) {
val componentType = psiType.componentType.canonicalText
if ("java.lang.Byte" == componentType) {
return true
}
return DomaClassName.BYTE.className == componentType
}
return true
// TODO If the condition does not match, return false to strengthen type checking.
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiType
import com.intellij.psi.search.GlobalSearchScope
import org.domaframework.doma.intellij.common.util.DomaClassName

class PsiClassTypeUtil {
companion object {
Expand All @@ -28,7 +29,7 @@ class PsiClassTypeUtil {
): Boolean {
val iterableType =
PsiType.getTypeByName(
"java.lang.Iterable",
DomaClassName.ITERABLE.className,
project,
GlobalSearchScope.allScope(project),
)
Expand Down Expand Up @@ -68,15 +69,15 @@ class PsiClassTypeUtil {
val resolved = daoParamType.resolve()
val optionalTypeMap =
mapOf(
"java.util.OptionalInt" to "java.lang.Integer",
"java.util.OptionalDouble" to "java.lang.Double",
"java.util.OptionalLong" to "java.lang.Long",
DomaClassName.OPTIONAL_INT.className to DomaClassName.INTEGER.className,
DomaClassName.OPTIONAL_DOUBLE.className to DomaClassName.DOUBLE.className,
DomaClassName.OPTIONAL_LONG.className to DomaClassName.LONG.className,
)
if (resolved != null) {
when (resolved.qualifiedName) {
// If the type is java.util.Optional, return its parameter type if available;
// otherwise, return the original daoParamType.
"java.util.Optional" -> return daoParamType.parameters.firstOrNull()
DomaClassName.OPTIONAL.className -> return daoParamType.parameters.firstOrNull()
?: daoParamType

// For primitive Optional types (e.g., OptionalInt, OptionalDouble),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiMethod
import org.domaframework.doma.intellij.common.dao.findDaoMethod
import org.domaframework.doma.intellij.common.psi.PsiTypeChecker
import org.domaframework.doma.intellij.extension.psi.searchParameter

class LiteralDirectiveHandler(
Expand All @@ -39,9 +38,7 @@ class LiteralDirectiveHandler(
) { daoMethod, bind ->
daoMethod
?.searchParameter(bind)
?.filter {
PsiTypeChecker.isTargetType(it.type)
}?.map { param -> VariableLookupItem(param) }
?.map { param -> VariableLookupItem(param) }
?.toList()
?: emptyList()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright Doma Tools Authors
*
* 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
*
* https://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.domaframework.doma.intellij.common.util

enum class DomaClassName(
val className: String,
) {
OPTIONAL("java.util.Optional"),
OPTIONAL_INT("java.util.OptionalInt"),
OPTIONAL_DOUBLE("java.util.OptionalDouble"),
OPTIONAL_LONG("java.util.OptionalLong"),

INTEGER("java.lang.Integer"),
DOUBLE("java.lang.Double"),
LONG("java.lang.Long"),

MAP("java.util.Map"),
LIST("java.util.List"),
ITERABLE("java.lang.Iterable"),

DOMAIN("org.seasar.doma.Domain"),
BI_FUNCTION("java.util.function.BiFunction"),
CONFIG("org.seasar.doma.jdbc.Config"),
PREPARED_SQL("org.seasar.doma.jdbc.PreparedSql"),
VOID("java.lang.Void"),
RETURNING("org.seasar.doma.Returning"),
REFERENCE("org.seasar.doma.jdbc.Reference"),

STRING("java.lang.String"),
OBJECT("java.lang.Object"),
BIG_DECIMAL("java.math.BigDecimal"),
BIG_INTEGER("java.math.BigInteger"),
LOCAL_DATE("java.time.LocalDate"),
LOCAL_TIME("java.time.LocalTime"),
LOCAL_DATE_TIME("java.time.LocalDateTime"),
SQL_DATE("java.sql.Date"),
SQL_TIME("java.sql.Time"),
SQL_TIMESTAMP("java.sql.Timestamp"),
SQL_ARRAY("java.sql.Array"),
SQL_BLOB("java.sql.Blob"),
SQL_CLOB("java.sql.Clob"),
SQL_XML("java.sql.SQLXML"),
UTIL_DATE("java.util.Date"),

BYTE("java.lang.Byte"),
SHORT("java.lang.Short"),
FLOAT("java.lang.Float"),
BOOLEAN("java.lang.Boolean"),

JAVA_FUNCTION("java.util.function.Function"),
JAVA_COLLECTOR("java.util.stream.Collector"),
JAVA_STREAM("java.util.stream.Stream"),
SELECT_TYPE("org.seasar.doma.SelectType"),

ENTITY("org.seasar.doma.Entity"),
;

fun isTargetClassNameStartsWith(paramTypeCanonicalNames: String): Boolean = paramTypeCanonicalNames.startsWith(this.className)

fun getGenericParamCanonicalText(vararg genericParas: String): String = "${this.className}<${genericParas.joinToString(", ")}>"

companion object {
fun isOptionalWrapperType(paramTypeCanonicalName: String): Boolean =
paramTypeCanonicalName in
listOf(
OPTIONAL_INT.className,
OPTIONAL_DOUBLE.className,
OPTIONAL_LONG.className,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright Doma Tools Authors
*
* 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
*
* https://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.domaframework.doma.intellij.common.validation.result

import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.domaframework.doma.intellij.common.psi.PsiParentClass
import org.domaframework.doma.intellij.common.util.DomaClassName

class ValidationMethodBiFunctionParamResult(
override val identify: PsiElement?,
override val shortName: String = "",
private val index: Int,
) : ValidationResult(identify, null, shortName) {
override fun setHighlight(
highlightRange: TextRange,
identify: PsiElement,
holder: ProblemsHolder,
parent: PsiParentClass?,
) {
val project = identify.project
holder.registerProblem(
identify,
getCheckParamIndexMessage(index),
problemHighlightType(project, shortName),
highlightRange,
)
}

private fun getCheckParamIndexMessage(index: Int): String =
when (index) {
0 -> "The first type argument of BiFunction must be ${DomaClassName.CONFIG.className}"
1 -> "The second type argument of BiFunction must be ${DomaClassName.PREPARED_SQL.className}"

else -> "The type argument at index $index is not supported"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Doma Tools Authors
*
* 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
*
* https://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.domaframework.doma.intellij.common.validation.result

import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.domaframework.doma.intellij.bundle.MessageBundle
import org.domaframework.doma.intellij.common.psi.PsiParentClass
import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType

class ValidationMethodHasRequireClassParamResult(
override val identify: PsiElement?,
override val shortName: String = "",
private val annotationType: DomaAnnotationType,
private val requiredClass: String,
) : ValidationResult(identify, null, shortName) {
override fun setHighlight(
highlightRange: TextRange,
identify: PsiElement,
holder: ProblemsHolder,
parent: PsiParentClass?,
) {
val project = identify.project
holder.registerProblem(
identify,
MessageBundle.message("inspection.invalid.dao.params.require.type", annotationType.name, requiredClass),
problemHighlightType(project, shortName),
highlightRange,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Doma Tools Authors
*
* 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
*
* https://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.domaframework.doma.intellij.common.validation.result

import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.domaframework.doma.intellij.bundle.MessageBundle
import org.domaframework.doma.intellij.common.psi.PsiParentClass

class ValidationMethodNotSelectStreamParamResult(
override val identify: PsiElement?,
override val shortName: String = "",
) : ValidationResult(identify, null, shortName) {
override fun setHighlight(
highlightRange: TextRange,
identify: PsiElement,
holder: ProblemsHolder,
parent: PsiParentClass?,
) {
val project = identify.project
holder.registerProblem(
identify,
MessageBundle.message("inspection.invalid.dao.select.param.notFound.strategy.stream"),
problemHighlightType(project, shortName),
highlightRange,
)
}
}
Loading
Loading