diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/GenerateSqlAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/GenerateSqlAction.kt index 8a9204ce..ef1093c3 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/GenerateSqlAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/GenerateSqlAction.kt @@ -22,10 +22,10 @@ import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.psi.PsiFile import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.getDaoClass import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil /** * Action class that generates SQL from Dao method diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/JumpToSQLFromDaoAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/JumpToSQLFromDaoAction.kt index fca4ecdd..7731b4b2 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/JumpToSQLFromDaoAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/JumpToSQLFromDaoAction.kt @@ -22,11 +22,11 @@ import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.psi.PsiFile import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.getDaoClass import org.domaframework.doma.intellij.common.dao.jumpSqlFromDao import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil class JumpToSQLFromDaoAction : AnAction() { private var currentFile: PsiFile? = null diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/sql/JumpToDaoFromSQLAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/sql/JumpToDaoFromSQLAction.kt index f57737c2..4e3306f2 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/sql/JumpToDaoFromSQLAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/sql/JumpToDaoFromSQLAction.kt @@ -20,11 +20,11 @@ import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.psi.PsiFile -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.findDaoFile import org.domaframework.doma.intellij.common.dao.findDaoMethod import org.domaframework.doma.intellij.common.dao.jumpToDaoMethod import org.domaframework.doma.intellij.common.isSupportFileType +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil /*** * Action to jump from SQL file to corresponding Dao function diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/foritem/ForDeclarationDaoBaseItem.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/foritem/ForDeclarationDaoBaseItem.kt deleted file mode 100644 index 5bc9f79e..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/foritem/ForDeclarationDaoBaseItem.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.sql.foritem - -import com.intellij.psi.PsiParameter -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.validator.SqlElFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.psi.SqlElIdExpr - -/** - * In the For directive, define the element based on the Dao parameter. - * %for [ForItem] : [ForDeclarationDaoBaseItem] - * %for [ForItem] : [ForDeclarationItem] - * ... - */ -open class ForDeclarationDaoBaseItem( - private val blocks: List, - val daoParameter: PsiParameter? = null, - open val index: Int = 0, -) : ForDeclarationItem(blocks.first()) { - /*** - * Obtain the type information of the For item defined from the Dao parameters. - */ - open fun getPsiParentClass(): PsiParentClass? { - val daoParamFieldValidator = - SqlElFieldAccessorChildElementValidator( - blocks, - element.containingFile, - topDaoParameter = daoParameter, - ) - - var lastType: PsiParentClass? = null - daoParamFieldValidator.validateChildren( - complete = { parent -> - lastType = parent - }, - ) - if (lastType == null) return null - return lastType - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/foritem/ForDeclarationStaticFieldAccessorItem.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/foritem/ForDeclarationStaticFieldAccessorItem.kt deleted file mode 100644 index 2946b702..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/foritem/ForDeclarationStaticFieldAccessorItem.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.sql.foritem - -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.cleanString -import org.domaframework.doma.intellij.common.sql.validator.SqlElStaticFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.psi.SqlElIdExpr -import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr - -/** - * In the For directive, define the element based on the Dao parameter. - * %for [ForItem] : [ForDeclarationStaticFieldAccessorItem] - * %for [ForItem] : [ForDeclarationItem] - * ... - */ -class ForDeclarationStaticFieldAccessorItem( - private val blocks: List, - private val staticFieldAccessExpr: SqlElStaticFieldAccessExpr, - override val index: Int = 0, - private val shortName: String = "", -) : ForDeclarationDaoBaseItem(blocks, null, index) { - /*** - * Obtain the type information of the For item defined from the Dao parameters. - */ - override fun getPsiParentClass(): PsiParentClass? { - val staticFieldValidator = - SqlElStaticFieldAccessorChildElementValidator( - blocks, - staticFieldAccessExpr, - shortName = shortName, - ) - - var lastPsiParentClass: PsiParentClass? = null - staticFieldValidator.validateChildren( - complete = { parent -> - lastPsiParentClass = parent - }, - ) - if (lastPsiParentClass == null) return null - - if (blocks.size == 1) { - val searchElementText = cleanString(blocks.first().text) - val field = lastPsiParentClass.findField(searchElementText) - if (field != null) { - val fieldType = field.type - return PsiParentClass(fieldType) - } - - val method = lastPsiParentClass.findMethod(searchElementText) - if (method != null) { - val methodReturnType = method.returnType ?: return null - return PsiParentClass(methodReturnType) - } - } - return lastPsiParentClass - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElChildElementValidator.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElChildElementValidator.kt deleted file mode 100644 index 17a0dd0c..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElChildElementValidator.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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.sql.validator - -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiClassType -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiType -import com.intellij.psi.util.elementType -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil -import org.domaframework.doma.intellij.common.sql.cleanString -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult -import org.domaframework.doma.intellij.psi.SqlElIdExpr -import org.domaframework.doma.intellij.psi.SqlElParameters -import org.domaframework.doma.intellij.psi.SqlTypes - -abstract class SqlElChildElementValidator( - open val blocks: List, - open val shortName: String = "", -) { - abstract fun validateChildren(dropIndex: Int = 0): ValidationResult? - - open fun validateChildren( - dropIndex: Int = 0, - findFieldMethod: (PsiType) -> PsiParentClass = { type: PsiType -> PsiParentClass(type) }, - complete: (PsiParentClass) -> Unit, - ): ValidationResult? = null - - fun validateFieldAccess( - project: Project, - topParent: PsiParentClass, - dropLastIndex: Int = 0, - findFieldMethod: ((PsiType) -> PsiParentClass)? = { type -> PsiParentClass(type) }, - complete: ((PsiParentClass) -> Unit) = { parent: PsiParentClass? -> }, - ): ValidationResult? = - getFieldAccessParentClass( - project, - topParent, - dropLastIndex, - findFieldMethod = findFieldMethod, - complete = complete, - ) - - protected fun getFieldAccessParentClass( - project: Project, - topParent: PsiParentClass, - dropLastIndex: Int = 0, - findFieldMethod: ((PsiType) -> PsiParentClass)? = { type -> PsiParentClass(type) }, - complete: ((PsiParentClass) -> Unit) = { parent: PsiParentClass? -> }, - ): ValidationResult? { - var parent = topParent - val parentType = parent.type - val classType = parentType as? PsiClassType ?: return null - - var competeResult: ValidationCompleteResult? = null - - if (dropLastIndex > 0 && blocks.size <= dropLastIndex + 1) { - return ValidationCompleteResult( - blocks.last(), - parent, - ) - } - - var parentListBaseType: PsiType? = - if (PsiClassTypeUtil.isIterableType(classType, project)) { - parentType - } else { - null - } - var listParamIndex = 0 - for (element in blocks.drop(1).dropLast(dropLastIndex)) { - val searchElm = cleanString(getSearchElementText(element)) - if (searchElm.isEmpty()) { - complete.invoke(parent) - return ValidationCompleteResult( - element, - parent, - ) - } - - val field = - parent - .findField(searchElm) - ?.let { match -> - val type = - parentListBaseType?.let { PsiClassTypeUtil.getParameterType(project, match.type, it, listParamIndex) } - ?: match.type - val classType = type as? PsiClassType - if (classType != null && PsiClassTypeUtil.isIterableType(classType, element.project)) { - parentListBaseType = type - listParamIndex = 0 - } - findFieldMethod?.invoke(type) - } - val method = - parent - .findMethod(searchElm) - ?.let { match -> - val returnType = match.returnType ?: return null - val methodReturnType = - parentListBaseType?.let { - PsiClassTypeUtil.getParameterType( - project, - returnType, - it, - listParamIndex, - ) - } - ?: returnType - val classType = methodReturnType as? PsiClassType - if (classType != null && - PsiClassTypeUtil.isIterableType( - classType, - element.project, - ) - ) { - parentListBaseType = methodReturnType - listParamIndex = 0 - } - findFieldMethod?.invoke(methodReturnType) - } - listParamIndex++ - if (field == null && method == null) { - return ValidationPropertyResult( - element, - parent, - shortName, - ) - } else { - if (field != null && element.nextSibling !is SqlElParameters) { - parent = field - competeResult = - ValidationCompleteResult( - element, - parent, - ) - } else if (method != null) { - parent = method - competeResult = - ValidationCompleteResult( - element, - parent, - ) - } - } - } - complete.invoke(parent) - return competeResult - } - - private fun getSearchElementText(elm: PsiElement): String = - if (elm is SqlElIdExpr || elm.elementType == SqlTypes.EL_IDENTIFIER) { - elm.text - } else { - "" - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElFieldAccessorChildElementValidator.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElFieldAccessorChildElementValidator.kt deleted file mode 100644 index 192702ad..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElFieldAccessorChildElementValidator.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.sql.validator - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiParameter -import com.intellij.psi.PsiType -import org.domaframework.doma.intellij.common.dao.findDaoMethod -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult -import org.domaframework.doma.intellij.extension.psi.findParameter -import org.domaframework.doma.intellij.extension.psi.getDomaAnnotationType -import org.domaframework.doma.intellij.extension.psi.getIterableClazz - -class SqlElFieldAccessorChildElementValidator( - override val blocks: List, - private val file: PsiFile, - override val shortName: String = "", - private val topDaoParameter: PsiParameter? = null, -) : SqlElChildElementValidator(blocks, shortName) { - val project = file.project - - override fun validateChildren( - dropIndex: Int, - findFieldMethod: (PsiType) -> PsiParentClass, - complete: (PsiParentClass) -> Unit, - ): ValidationResult? { - val parentClassResult = getParentClass() - val parentClass = parentClassResult?.parentClass - if (parentClassResult is ValidationCompleteResult && parentClass != null) { - return validateFieldAccess( - project, - parentClass, - complete = complete, - ) - } - - return parentClassResult - } - - override fun validateChildren(dropIndex: Int): ValidationResult? { - val parentClassResult = getParentClass() - val parentClass = parentClassResult?.parentClass - if (parentClassResult is ValidationCompleteResult && parentClass != null) { - return validateFieldAccess(project, parentClass) - } - - return parentClassResult - } - - private fun getParentClass(): ValidationResult? { - val daoMethod = findDaoMethod(file) ?: return null - val topElement: PsiElement = blocks.firstOrNull() ?: return null - val validDaoParam = - topDaoParameter - ?: daoMethod.findParameter(topElement.text) - - if (validDaoParam == null) { - return ValidationDaoParamResult( - topElement, - daoMethod.name, - shortName, - ) - } - - return ValidationCompleteResult( - topElement, - validDaoParam.getIterableClazz(daoMethod.getDomaAnnotationType()), - ) - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElForItemFieldAccessorChildElementValidator.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElForItemFieldAccessorChildElementValidator.kt deleted file mode 100644 index a46b1db8..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElForItemFieldAccessorChildElementValidator.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.sql.validator - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiType -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult - -class SqlElForItemFieldAccessorChildElementValidator( - override val blocks: List, - private val declarationType: PsiParentClass, - override val shortName: String = "", -) : SqlElChildElementValidator(blocks, shortName) { - val project = blocks.firstOrNull()?.project - - override fun validateChildren( - dropIndex: Int, - findFieldMethod: (PsiType) -> PsiParentClass, - complete: (PsiParentClass) -> Unit, - ): ValidationResult? = - project?.let { - validateFieldAccess( - it, - declarationType, - dropIndex, - complete = complete, - ) - } - - override fun validateChildren(dropIndex: Int): ValidationResult? = project?.let { validateFieldAccess(it, declarationType, dropIndex) } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElStaticFieldAccessorChildElementValidator.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElStaticFieldAccessorChildElementValidator.kt deleted file mode 100644 index 3be67667..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/SqlElStaticFieldAccessorChildElementValidator.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.sql.validator - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiType -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.psi.PsiStaticElement -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationIgnoreResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult -import org.domaframework.doma.intellij.extension.expr.fqdn -import org.domaframework.doma.intellij.extension.psi.psiClassType -import org.domaframework.doma.intellij.psi.SqlElParameters -import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr - -class SqlElStaticFieldAccessorChildElementValidator( - override val blocks: List, - private val staticAccuser: SqlElStaticFieldAccessExpr, - override val shortName: String = "", -) : SqlElChildElementValidator(blocks, shortName) { - val project = staticAccuser.containingFile.project - - override fun validateChildren( - dropIndex: Int, - findFieldMethod: (PsiType) -> PsiParentClass, - complete: (PsiParentClass) -> Unit, - ): ValidationResult? { - val getParentResult = getFieldTopParent() - when (getParentResult) { - is ValidationCompleteResult -> { - val parent = getParentResult.parentClass - return validateFieldAccess( - project, - parent, - dropLastIndex = dropIndex, - complete = complete, - ) - } - is ValidationIgnoreResult -> return null - else -> return getParentResult - } - } - - override fun validateChildren(dropIndex: Int): ValidationResult? { - val getParentResult = getFieldTopParent() - when (getParentResult) { - is ValidationCompleteResult -> { - val parent = getParentResult.parentClass - return validateFieldAccess(project, parent, dropLastIndex = dropIndex) - } - is ValidationIgnoreResult -> return null - else -> return getParentResult - } - } - - fun getClassType(): PsiParentClass? { - val fqdn = staticAccuser.fqdn - val file = staticAccuser.containingFile - val psiStaticElement = PsiStaticElement(fqdn, file) - val clazz = psiStaticElement.getRefClazz() ?: return null - - return PsiParentClass(clazz.psiClassType) - } - - private fun getFieldTopParent(): ValidationResult? { - var parent = getClassType() ?: return ValidationIgnoreResult(null) - val staticTopElement = - blocks.firstOrNull() - ?: return ValidationCompleteResult(staticAccuser.elClass, parent) - val nextSibling = staticTopElement.nextSibling - val topField = - if (nextSibling !is SqlElParameters) { - parent.findStaticField(staticTopElement.text) - } else { - null - } - val topMethod = - if (nextSibling is SqlElParameters) { - parent.findStaticMethod(staticTopElement.text) - } else { - null - } - if (topField == null && topMethod == null) { - return ValidationPropertyResult( - staticTopElement, - parent, - shortName, - ) - } - - (topField?.type ?: topMethod?.returnType) - ?.let { parent = PsiParentClass(it) } - ?: return ValidationIgnoreResult(staticTopElement) - - return ValidationCompleteResult(staticTopElement, parent) - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationIgnoreResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationIgnoreResult.kt deleted file mode 100644 index c0e9ed63..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationIgnoreResult.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.sql.validator.result - -import com.intellij.codeInspection.ProblemsHolder -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiElement -import org.domaframework.doma.intellij.common.psi.PsiParentClass - -class ValidationIgnoreResult( - override val identify: PsiElement?, -) : ValidationResult(identify, null, "") { - override fun setHighlight( - highlightRange: TextRange, - identify: PsiElement, - holder: ProblemsHolder, - parent: PsiParentClass?, - project: Project, - ) { - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt new file mode 100644 index 00000000..d26a65b4 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt @@ -0,0 +1,454 @@ +/* + * 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 + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import com.intellij.psi.util.CachedValue +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.elementType +import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil +import org.domaframework.doma.intellij.common.sql.cleanString +import org.domaframework.doma.intellij.common.sql.foritem.ForItem +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult +import org.domaframework.doma.intellij.extension.expr.accessElements +import org.domaframework.doma.intellij.extension.psi.findParameter +import org.domaframework.doma.intellij.extension.psi.findStaticField +import org.domaframework.doma.intellij.extension.psi.findStaticMethod +import org.domaframework.doma.intellij.extension.psi.getForItem +import org.domaframework.doma.intellij.extension.psi.getForItemDeclaration +import org.domaframework.doma.intellij.psi.SqlElForDirective +import org.domaframework.doma.intellij.psi.SqlElIdExpr +import org.domaframework.doma.intellij.psi.SqlElParameters +import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr +import org.domaframework.doma.intellij.psi.SqlTypes + +class ForDirectiveUtil { + data class BlockToken( + val type: BlockType, + val item: PsiElement, + val position: Int, + ) + + enum class BlockType { + FOR, + IF, + END, + } + + companion object { + private val cachedForDirectiveBlocks: MutableMap>> = + mutableMapOf() + + /** + * Get the parent for directive list to which this directive belongs + * @param targetElement Element to search for definer + * @param skipSelf Pass true if the for directory definition element name itself is to search for the definition source. + * @return parent for directive list to which this directive belongs + */ + fun getForDirectiveBlocks( + targetElement: PsiElement, + skipSelf: Boolean = true, + ): List { + val cachedValue = + cachedForDirectiveBlocks.getOrPut(targetElement) { + CachedValuesManager.getManager(targetElement.project).createCachedValue { + val file = targetElement.containingFile ?: return@createCachedValue null + val directiveBlocks = + PsiTreeUtil + .findChildrenOfType(file, PsiElement::class.java) + .filter { elm -> + ( + elm.elementType == SqlTypes.EL_FOR || + elm.elementType == SqlTypes.EL_IF || + elm.elementType == SqlTypes.EL_END + ) + }.sortedBy { it.textOffset } + .map { + when (it.elementType) { + SqlTypes.EL_FOR -> { + val item = + (it.parent as? SqlElForDirective)?.getForItem() + BlockToken( + BlockType.FOR, + item ?: it, + item?.textOffset ?: 0, + ) + } + + SqlTypes.EL_IF -> + BlockToken( + BlockType.IF, + it, + it.textOffset, + ) + + else -> BlockToken(BlockType.END, it, it.textOffset) + } + } + var stack = mutableListOf() + val filterPosition = + if (skipSelf) { + directiveBlocks.filter { + it.position < targetElement.textOffset + } + } else { + directiveBlocks.filter { it.position <= targetElement.textOffset } + } + filterPosition.forEach { block -> + when (block.type) { + BlockType.FOR, BlockType.IF -> stack.add(block) + BlockType.END -> if (stack.isNotEmpty()) stack.removeAt(stack.lastIndex) + } + } + if (skipSelf) { + stack = + stack + .filter { !isSameForDirective(it.item, targetElement) } + .toMutableList() + } + + CachedValueProvider.Result.create( + stack.filter { it.type == BlockType.FOR }, + file, + ) + } + } + return cachedValue.value + } + + private fun isSameForDirective( + token: PsiElement, + targetElement: PsiElement, + ): Boolean = + PsiTreeUtil.getParentOfType( + token, + SqlElForDirective::class.java, + ) == + PsiTreeUtil.getParentOfType( + targetElement, + SqlElForDirective::class.java, + ) + + fun findForItem( + targetElement: PsiElement, + skipSelf: Boolean = true, + forDirectives: List = getForDirectiveBlocks(targetElement, skipSelf), + ): PsiElement? { + val searchText = targetElement.text.replace("_has_next", "").replace("_index", "") + return forDirectives.firstOrNull { it.item.text == searchText }?.item + } + + /** + * for directive Loop through a list and get its own defined type + * Used to get the type information of the top of the bind variable or the for item itself + * @param project Project + * @param forDirectiveBlocks List of for directive blocks + */ + fun getForDirectiveItemClassType( + project: Project, + forDirectiveBlocks: List, + ): PsiParentClass? { + // Get the type of the top for directive definition element + // Defined in Dao parameters or static property calls + if (forDirectiveBlocks.isEmpty()) return null + var parentClassType = + getTopForDirectiveDeclarationClassType( + forDirectiveBlocks.first().item, + project, + ) ?: return null + forDirectiveBlocks.drop(1).forEach { directive -> + // Get the definition type of the target directive + val formItem = ForItem(directive.item) + val forDirectiveExpr = formItem.getParentForDirectiveExpr() + val forDirectiveDeclaration = forDirectiveExpr?.getForItemDeclaration() + if (forDirectiveDeclaration != null) { + val forItemDeclarationBlocks = forDirectiveDeclaration.getDeclarationChildren() + getFieldAccessLastPropertyClassType( + forItemDeclarationBlocks, + project, + parentClassType, + complete = { lastType -> + val classType = lastType.type as? PsiClassType + val nestClass = + if (classType != null && + PsiClassTypeUtil.Companion.isIterableType(classType, project) + ) { + classType.parameters.firstOrNull() + } else { + null + } + nestClass?.let { parentClassType = PsiParentClass(it) } + }, + ) + } + } + + return parentClassType + } + + fun getTopForDirectiveDeclarationClassType( + topForDirectiveItem: PsiElement, + project: Project, + ): PsiParentClass? { + var result: PsiParentClass? = null + var fieldAccessTopParentClass: PsiParentClass? = null + + val forDirectiveExpr = + PsiTreeUtil.getParentOfType(topForDirectiveItem, SqlElForDirective::class.java) + val forDirectiveDeclaration = forDirectiveExpr?.getForItemDeclaration() + if (forDirectiveDeclaration != null) { + var isBatchAnnotation = false + val forItemDeclarationBlocks = + if (forDirectiveDeclaration.element is SqlElStaticFieldAccessExpr) { + val staticFieldAccessExpr = forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr + staticFieldAccessExpr.accessElements + } else { + forDirectiveDeclaration.getDeclarationChildren() + } + + // Defined by StaticFieldAccess + if (forDirectiveDeclaration.element is SqlElStaticFieldAccessExpr) { + val file = topForDirectiveItem.containingFile + val staticFieldAccessExpr = forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr + val clazz = staticFieldAccessExpr.elClass + val staticElement = PsiStaticElement(clazz.elIdExprList, file) + val referenceClazz = staticElement.getRefClazz() ?: return null + + // In the case of staticFieldAccess, the property that is called first is retrieved. + fieldAccessTopParentClass = getStaticFieldAccessTopElementClassType(staticFieldAccessExpr, referenceClazz) + } else { + // Defined by Dao parameter + val file = topForDirectiveItem.containingFile ?: return null + val daoMethod = findDaoMethod(file) ?: return null + val topElementText = forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text ?: return null + isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() + + val matchParam = daoMethod.findParameter(cleanString(topElementText)) + val daoParamType = matchParam?.type ?: return null + fieldAccessTopParentClass = PsiParentClass(daoParamType) + } + fieldAccessTopParentClass?.let { + getFieldAccessLastPropertyClassType( + forItemDeclarationBlocks, + topForDirectiveItem.project, + it, + isBatchAnnotation = isBatchAnnotation, + complete = { lastType -> + val classType = lastType.type as? PsiClassType + val nestClass = + if (classType != null && + PsiClassTypeUtil.Companion.isIterableType(classType, project) + ) { + classType.parameters.firstOrNull() + } else { + null + } + nestClass?.let { result = PsiParentClass(it) } + }, + ) + } + } + return result + } + + fun getStaticFieldAccessTopElementClassType( + staticFieldAccessExpr: SqlElStaticFieldAccessExpr, + referenceClazz: PsiClass, + ): PsiParentClass? { + val topElement = staticFieldAccessExpr.elIdExprList.firstOrNull() ?: return null + val searchText = cleanString(topElement.text) + if (topElement.nextSibling?.elementType != SqlTypes.EL_PARAMETERS) { + val topPropertyField = referenceClazz.findStaticField(searchText) + topPropertyField?.type?.let { + return PsiParentClass(it) + } + } else { + val topPropertyMethod = referenceClazz.findStaticMethod(searchText) + topPropertyMethod?.let { + val returnType = it.returnType ?: return null + return PsiParentClass(returnType) + } + } + return null + } + + /** + * Analyzes the element's field accesses and gets the type of the last property being called + * @param blocks List of elements to be analyzed + * @param project Project + * @param topParent The parent class of the element to be analyzed(DaoParam, forDirectiveItem, StaticFieldAccess) + * @param shortName Inspection class shortName + * @param dropLastIndex The number of elements to exclude from the end of the block to search + * @param findFieldMethod Callback when property is found + * @param complete Callback when the analysis is completed + * @return ValidationResult + */ + fun getFieldAccessLastPropertyClassType( + blocks: List, + project: Project, + topParent: PsiParentClass, + isBatchAnnotation: Boolean = false, + shortName: String = "", + dropLastIndex: Int = 0, + findFieldMethod: ((PsiType) -> PsiParentClass)? = { type -> PsiParentClass(type) }, + complete: ((PsiParentClass) -> Unit) = { parent: PsiParentClass? -> }, + ): ValidationResult? { + var parent = + if (isBatchAnnotation) { + val parentType = topParent.type + val nextClassType = parentType as? PsiClassType ?: return null + val nestType = nextClassType.parameters.firstOrNull() ?: return null + PsiParentClass(nestType) + } else { + topParent + } + val parentType = parent.type + val classType = parentType as? PsiClassType ?: return null + + var competeResult: ValidationCompleteResult? = null + + val searchBlocks = blocks.drop(1).dropLast(dropLastIndex) + if (dropLastIndex > 0 && searchBlocks.isEmpty()) { + complete.invoke(parent) + return ValidationCompleteResult( + blocks.last(), + parent, + ) + } + + // When a List type element is used as the parent, + // the original declared type is retained and the referenced type is obtained by nesting. + var parentListBaseType: PsiType? = + if (PsiClassTypeUtil.Companion.isIterableType(classType, project)) { + parentType + } else { + null + } + var nestIndex = 0 + + // Analyze a block element and get the type of the last called property + for (element in searchBlocks) { + val searchElm = cleanString(getSearchElementText(element)) + if (searchElm.isEmpty()) { + complete.invoke(parent) + return ValidationCompleteResult( + element, + parent, + ) + } + + val field = + parent + .findField(searchElm) + ?.let { match -> + val type = + parentListBaseType?.let { + PsiClassTypeUtil.Companion.getParameterType( + project, + match.type, + it, + nestIndex, + ) + } + ?: match.type + val classType = type as? PsiClassType + if (classType != null && + PsiClassTypeUtil.Companion.isIterableType( + classType, + element.project, + ) + ) { + parentListBaseType = type + nestIndex = 0 + } + findFieldMethod?.invoke(type) + } + val method = + parent + .findMethod(searchElm) + ?.let { match -> + val returnType = match.returnType ?: return null + val methodReturnType = + parentListBaseType?.let { + PsiClassTypeUtil.Companion.getParameterType( + project, + returnType, + it, + nestIndex, + ) + } + ?: returnType + val classType = methodReturnType as? PsiClassType + if (classType != null && + PsiClassTypeUtil.Companion.isIterableType( + classType, + element.project, + ) + ) { + parentListBaseType = methodReturnType + nestIndex = 0 + } + findFieldMethod?.invoke(methodReturnType) + } + nestIndex++ + if (field == null && method == null) { + return ValidationPropertyResult( + element, + parent, + shortName, + ) + } + + if (field != null && element.nextSibling !is SqlElParameters) { + parent = field + competeResult = + ValidationCompleteResult( + element, + parent, + ) + } else if (method != null) { + parent = method + competeResult = + ValidationCompleteResult( + element, + parent, + ) + } + } + complete.invoke(parent) + return competeResult + } + + private fun getSearchElementText(elm: PsiElement): String = + if (elm is SqlElIdExpr || elm.elementType == SqlTypes.EL_IDENTIFIER) { + elm.text + } else { + "" + } + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/PluginLoggerUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/PluginLoggerUtil.kt similarity index 97% rename from src/main/kotlin/org/domaframework/doma/intellij/common/PluginLoggerUtil.kt rename to src/main/kotlin/org/domaframework/doma/intellij/common/util/PluginLoggerUtil.kt index d9459c13..8ae35c65 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/PluginLoggerUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/PluginLoggerUtil.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.common +package org.domaframework.doma.intellij.common.util import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/PluginUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/PluginUtil.kt similarity index 95% rename from src/main/kotlin/org/domaframework/doma/intellij/common/PluginUtil.kt rename to src/main/kotlin/org/domaframework/doma/intellij/common/util/PluginUtil.kt index 0e2aacb7..fc0a5bcc 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/PluginUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/PluginUtil.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.common +package org.domaframework.doma.intellij.common.util import com.intellij.ide.plugins.PluginManagerCore import com.intellij.openapi.extensions.PluginId diff --git a/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt index fef231e5..b02688fd 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt @@ -32,15 +32,14 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import com.intellij.psi.util.prevLeafs import com.intellij.util.ProcessingContext -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.findDaoMethod import org.domaframework.doma.intellij.common.psi.PsiParentClass import org.domaframework.doma.intellij.common.psi.PsiPatternUtil import org.domaframework.doma.intellij.common.sql.cleanString import org.domaframework.doma.intellij.common.sql.directive.DirectiveCompletion -import org.domaframework.doma.intellij.common.sql.validator.SqlElForItemFieldAccessorChildElementValidator import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.extension.getJavaClazz import org.domaframework.doma.intellij.extension.psi.findNodeParent import org.domaframework.doma.intellij.extension.psi.findSelfBlocks @@ -52,7 +51,6 @@ import org.domaframework.doma.intellij.extension.psi.isNotWhiteSpace import org.domaframework.doma.intellij.extension.psi.searchParameter import org.domaframework.doma.intellij.extension.psi.searchStaticField import org.domaframework.doma.intellij.extension.psi.searchStaticMethod -import org.domaframework.doma.intellij.inspection.ForDirectiveInspection import org.domaframework.doma.intellij.psi.SqlElAndExpr import org.domaframework.doma.intellij.psi.SqlElClass import org.domaframework.doma.intellij.psi.SqlElElseifDirective @@ -300,39 +298,23 @@ class SqlParameterCompletionProvider : CompletionProvider( getElementTypeByFieldAccess(originalFile, elements, result) ?: return } - val fieldAccessorChildElementValidator = - SqlElForItemFieldAccessorChildElementValidator( - elements, - PsiParentClass(topElementType), - ) - var psiParentClass = PsiParentClass(topElementType) - var parentProperties: Array = psiParentClass.clazz?.allFields ?: emptyArray() - var parentMethods: Array = psiParentClass.clazz?.allMethods ?: emptyArray() - - val validateResult = - fieldAccessorChildElementValidator.validateChildren( - complete = { parent -> - parentProperties = - parent.searchField(searchText)?.toTypedArray() ?: emptyArray() - parentMethods = parent.searchMethod(searchText)?.toTypedArray() ?: emptyArray() - setFieldsAndMethodsCompletionResultSet( - parentProperties, - parentMethods, - result, - ) - }, - ) - - if (validateResult is ValidationPropertyResult) { - val parent = validateResult.parentClass ?: return - parent.searchField(searchText)?.let { - parentProperties = it.toTypedArray() - } ?: { parentProperties = emptyArray() } - val methods = parent.searchMethod(searchText) - parentMethods = methods?.toTypedArray() ?: emptyArray() - setFieldsAndMethodsCompletionResultSet(parentProperties, parentMethods, result) - } + // FieldAccess Completion + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + elements, + top.project, + psiParentClass, + shortName = "", + dropLastIndex = 1, + complete = { lastType -> + val searchWord = cleanString(getSearchElementText(position)) + setFieldsAndMethodsCompletionResultSet( + lastType.searchField(searchWord)?.toTypedArray() ?: emptyArray(), + lastType.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(), + result, + ) + }, + ) } private fun setStaticFieldAccess( @@ -394,6 +376,7 @@ class SqlParameterCompletionProvider : CompletionProvider( val matchParams = daoMethod.searchParameter(topText) val findParam = matchParams.find { it.name == topText } if (elements.size <= 1 && findParam == null) { + // TODO Add For Directive Items matchParams.map { match -> result.addElement(LookupElementBuilder.create(match.name)) } @@ -428,36 +411,36 @@ class SqlParameterCompletionProvider : CompletionProvider( } } + /** + * When accessing a field starting from a for item, refer to the defined type and call the property. + */ private fun isFieldAccessByForItem( top: PsiElement, elements: List, positionText: String, result: CompletionResultSet, ): Boolean { - val file = top.containingFile ?: return false - val daoMethod = findDaoMethod(file) ?: return false - val forDeclaration = ForDirectiveInspection(daoMethod) - val forItem = forDeclaration.getForItem(top) - if (forItem != null) { - val validateResult = forDeclaration.validateFieldAccessByForItem(elements) - if (validateResult is ValidationCompleteResult) { - val validator = - SqlElForItemFieldAccessorChildElementValidator( - elements, - validateResult.parentClass, - ) - val forValidationResult = validator.validateChildren(1)?.parentClass ?: return false + val project = top.project + val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(top) + ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false - val parentClass = forValidationResult - val searchWord = cleanString(positionText) - setFieldsAndMethodsCompletionResultSet( - parentClass.searchField(searchWord)?.toTypedArray() ?: emptyArray(), - parentClass.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(), - result, - ) - return true - } - } - return false + val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) ?: return false + val result = + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + elements, + project, + forItemClassType, + shortName = "", + dropLastIndex = 1, + complete = { lastType -> + val searchWord = cleanString(positionText) + setFieldsAndMethodsCompletionResultSet( + lastType.searchField(searchWord)?.toTypedArray() ?: emptyArray(), + lastType.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(), + result, + ) + }, + ) + return result is ValidationCompleteResult } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt index a184e0f6..c665a50c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt @@ -16,20 +16,25 @@ package org.domaframework.doma.intellij.document import com.intellij.lang.documentation.AbstractDocumentationProvider -import com.intellij.psi.PsiClassType +import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil +import org.domaframework.doma.intellij.common.psi.PsiStaticElement import org.domaframework.doma.intellij.common.sql.foritem.ForItem -import org.domaframework.doma.intellij.common.sql.validator.SqlElForItemFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.expr.accessElements +import org.domaframework.doma.intellij.extension.expr.accessElementsPrevOriginalElement +import org.domaframework.doma.intellij.extension.psi.findParameter import org.domaframework.doma.intellij.extension.psi.getForItem -import org.domaframework.doma.intellij.extension.psi.getForItemDeclaration -import org.domaframework.doma.intellij.inspection.ForDirectiveInspection -import org.domaframework.doma.intellij.psi.SqlElForDirective +import org.domaframework.doma.intellij.extension.psi.psiClassType +import org.domaframework.doma.intellij.psi.SqlElClass +import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlElIdExpr +import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlTypes import org.toml.lang.psi.ext.elementType import java.util.LinkedList @@ -46,112 +51,98 @@ class ForItemElementDocumentationProvider : AbstractDocumentationProvider() { originalElement, ) } - + val project = originalElement.project val file = originalElement.containingFile - val daoMethod = findDaoMethod(file) ?: return "" - val forDirectiveInspection = ForDirectiveInspection(daoMethod, "") - - val currentForItem = ForItem(originalElement) - val forDirectiveExpr = currentForItem.getParentForDirectiveExpr() - if (forDirectiveExpr != null) { - val declarationClassType = - forDirectiveInspection.validateFieldAccessByForItem( - listOf(originalElement), - skipSelf = false, - ) - if (declarationClassType != null) { - generateDocumentInForDirective( - forDirectiveInspection, - declarationClassType, - forDirectiveExpr, - originalElement, - result, - ) - } + + val staticFieldAccessExpr = + PsiTreeUtil.getParentOfType(originalElement, SqlElStaticFieldAccessExpr::class.java) + if (staticFieldAccessExpr != null) { + generateStaticFieldDocument( + staticFieldAccessExpr, + file, + originalElement, + project, + result, + ) } else { - generateDocumentInBindVariable(forDirectiveInspection, originalElement, result) + generateDaoFieldAccessDocument(originalElement, project, result) } return result.joinToString("\n") } - private fun generateDocumentInForItem( + private fun generateDaoFieldAccessDocument( originalElement: PsiElement, - declarationChildren: List, - declarationClassType: PsiParentClass, + project: Project, result: MutableList, ) { - val parentClass = declarationClassType - val forItemValidator = - SqlElForItemFieldAccessorChildElementValidator( - declarationChildren, - parentClass, + var topParentType: PsiParentClass? = null + val selfSkip = isSelfSkip(originalElement) + val forDirectives = ForDirectiveUtil.getForDirectiveBlocks(originalElement, selfSkip) + val fieldAccessExpr = + PsiTreeUtil.getParentOfType( + originalElement, + SqlElFieldAccessExpr::class.java, ) - forItemValidator.validateChildren(complete = { lastType -> - var resultParent = lastType - var classType: PsiClassType? = resultParent.type as? PsiClassType - if (classType != null && - PsiClassTypeUtil.isIterableType(classType, originalElement.project) - ) { - classType = classType.parameters.firstOrNull() as? PsiClassType - } - if (classType != null) { - resultParent = PsiParentClass(classType) - result.add("${generateTypeLink(resultParent)} ${originalElement.text}") - } - }) - } + val fieldAccessBlocks = + fieldAccessExpr?.accessElementsPrevOriginalElement(originalElement.textOffset) + val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement - private fun generateDocumentInBindVariable( - forDirectiveInspection: ForDirectiveInspection, - originalElement: PsiElement, - result: MutableList, - ) { - val declarationClassType = - forDirectiveInspection.validateFieldAccessByForItem(listOf(originalElement)) - if (declarationClassType != null) { - val parentClass = declarationClassType.parentClass - result.add("${generateTypeLink(parentClass)} ${originalElement.text}") - } - } - - private fun generateDocumentInForDirective( - forDirectiveInspection: ForDirectiveInspection, - declarationClassType: ValidationResult, - forDirectiveExpr: SqlElForDirective, - originalElement: PsiElement, - result: MutableList, - ) { - val parentClass = declarationClassType.parentClass - val parentType = parentClass?.type as? PsiClassType - - if (forDirectiveExpr.getForItem()?.textOffset != originalElement.textOffset) { - generateDocumentForItemSelf(parentType, originalElement, result) + var isBatchAnnotation = false + if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) { + topParentType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives) } else { - val declarationSide = forDirectiveExpr.getForItemDeclaration() ?: return - val children = declarationSide.getDeclarationChildren() - - val declarationClassType = - forDirectiveInspection.validateFieldAccessByForItem(listOf(originalElement), false) - if (declarationClassType is ValidationCompleteResult) { - generateDocumentInForItem( - originalElement, - children, - declarationClassType.parentClass, - result, + val daoMethod = findDaoMethod(originalElement.containingFile) ?: return + val param = daoMethod.findParameter(originalElement.text) ?: return + isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() + topParentType = PsiParentClass(param.type) + } + if (fieldAccessExpr != null && fieldAccessBlocks != null) { + topParentType?.let { + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + fieldAccessBlocks, + project, + it, + isBatchAnnotation = isBatchAnnotation, + complete = { lastType -> + result.add("${generateTypeLink(lastType)} ${originalElement.text}") + }, ) } + return } + result.add("${generateTypeLink(topParentType)} ${originalElement.text}") } - private fun generateDocumentForItemSelf( - parentType: PsiClassType?, + private fun generateStaticFieldDocument( + staticFieldAccessExpr: SqlElStaticFieldAccessExpr, + file: PsiFile, originalElement: PsiElement, + project: Project, result: MutableList, ) { - if (parentType != null) { - val forItemParentClassType = PsiParentClass(parentType) - result.add("${generateTypeLink(forItemParentClassType)} ${originalElement.text}") + val fieldAccessBlocks = staticFieldAccessExpr.accessElements + val staticElement = PsiStaticElement(fieldAccessBlocks, file) + val referenceClass = staticElement.getRefClazz() ?: return + if (PsiTreeUtil.getParentOfType(originalElement, SqlElClass::class.java) != null) { + val clazzType = PsiParentClass(referenceClass.psiClassType) + result.add("${generateTypeLink(clazzType)} ${originalElement.text}") + return } + + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + fieldAccessBlocks.filter { it.textOffset <= originalElement.textOffset }, + project, + PsiParentClass(referenceClass.psiClassType), + complete = { lastType -> + result.add("${generateTypeLink(lastType)} ${originalElement.text}") + }, + ) + } + + private fun isSelfSkip(targetElement: PsiElement): Boolean { + val forItem = ForItem(targetElement) + val forDirectiveExpr = forItem.getParentForDirectiveExpr() + return !(forDirectiveExpr != null && forDirectiveExpr.getForItem()?.textOffset == targetElement.textOffset) } override fun generateHoverDoc( diff --git a/src/main/kotlin/org/domaframework/doma/intellij/extension/expr/SqlElExtensions.kt b/src/main/kotlin/org/domaframework/doma/intellij/extension/expr/SqlElExtensions.kt index 91a60217..4783d98e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/extension/expr/SqlElExtensions.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/extension/expr/SqlElExtensions.kt @@ -15,11 +15,9 @@ */ package org.domaframework.doma.intellij.extension.expr -import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr -import org.domaframework.doma.intellij.psi.SqlElClass import org.domaframework.doma.intellij.psi.SqlElElseifDirective import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlElForDirective @@ -41,12 +39,8 @@ val SqlElFieldAccessExpr.accessElements: List .sortedBy { it.textOffset } .toList() -val SqlElStaticFieldAccessExpr.fqdn: String - get() { - val elClazz = PsiTreeUtil.getChildOfType(this, SqlElClass::class.java) ?: return "" - val fqdn = PsiTreeUtil.getChildrenOfTypeAsList(elClazz, PsiElement::class.java) - return fqdn.toList().joinToString("") { it.text } - } +fun SqlElFieldAccessExpr.accessElementsPrevOriginalElement(targetTextOffset: Int): List = + this.accessElements.filter { it != null && it.textOffset <= targetTextOffset }.mapNotNull { it } fun SqlCustomElCommentExpr.isConditionOrLoopDirective(): Boolean = PsiTreeUtil.getChildOfType(this, SqlElIfDirective::class.java) != null || diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt index 5af0a5cf..8174c63e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt @@ -28,7 +28,7 @@ import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import com.intellij.psi.util.prevLeafs -import org.domaframework.doma.intellij.common.PluginLoggerUtil +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.extension.expr.isConditionOrLoopDirective import org.domaframework.doma.intellij.psi.SqlBlockComment import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr diff --git a/src/main/kotlin/org/domaframework/doma/intellij/gutter/dao/DaoMethodProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/gutter/dao/DaoMethodProvider.kt index ab957023..30ca7a80 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/gutter/dao/DaoMethodProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/gutter/dao/DaoMethodProvider.kt @@ -26,11 +26,11 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.psi.PsiNameIdentifierOwner import org.domaframework.doma.intellij.bundle.MessageBundle -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.getDaoClass import org.domaframework.doma.intellij.common.dao.jumpSqlFromDao import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.setting.SqlIcon import java.awt.event.MouseEvent import javax.swing.Icon diff --git a/src/main/kotlin/org/domaframework/doma/intellij/gutter/sql/SqlLineMakerProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/gutter/sql/SqlLineMakerProvider.kt index 678386af..00f4ed85 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/gutter/sql/SqlLineMakerProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/gutter/sql/SqlLineMakerProvider.kt @@ -25,12 +25,12 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import org.domaframework.doma.intellij.bundle.MessageBundle -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.findDaoFile import org.domaframework.doma.intellij.common.dao.findDaoMethod import org.domaframework.doma.intellij.common.dao.jumpToDaoMethod import org.domaframework.doma.intellij.common.isInjectionSqlFile import org.domaframework.doma.intellij.common.isSupportFileType +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.jetbrains.kotlin.idea.core.util.toPsiFile import java.awt.event.MouseEvent import javax.swing.Icon diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/ForDirectiveInspection.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/ForDirectiveInspection.kt deleted file mode 100644 index 05d55d6e..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/ForDirectiveInspection.kt +++ /dev/null @@ -1,300 +0,0 @@ -/* - * 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.inspection - -import com.intellij.psi.PsiClassType -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.psi.util.CachedValue -import com.intellij.psi.util.CachedValueProvider -import com.intellij.psi.util.CachedValuesManager -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.psi.util.elementType -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil -import org.domaframework.doma.intellij.common.sql.foritem.ForDeclarationDaoBaseItem -import org.domaframework.doma.intellij.common.sql.foritem.ForDeclarationItem -import org.domaframework.doma.intellij.common.sql.foritem.ForDeclarationStaticFieldAccessorItem -import org.domaframework.doma.intellij.common.sql.foritem.ForDirectiveItemBase -import org.domaframework.doma.intellij.common.sql.foritem.ForItem -import org.domaframework.doma.intellij.common.sql.validator.SqlElForItemFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult -import org.domaframework.doma.intellij.extension.expr.accessElements -import org.domaframework.doma.intellij.extension.psi.findParameter -import org.domaframework.doma.intellij.extension.psi.getDomaAnnotationType -import org.domaframework.doma.intellij.extension.psi.getForItem -import org.domaframework.doma.intellij.extension.psi.getForItemDeclaration -import org.domaframework.doma.intellij.psi.SqlElForDirective -import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr -import org.domaframework.doma.intellij.psi.SqlTypes - -class ForDirectiveInspection( - private val daoMethod: PsiMethod, - private val shortName: String = "", -) { - data class BlockToken( - val type: BlockType, - val item: PsiElement, - val position: Int, - ) - - enum class BlockType { - FOR, - IF, - END, - } - - private val cachedForDirectiveBlocks: MutableMap>> = - mutableMapOf() - - fun getForItem( - targetElement: PsiElement, - skipSelf: Boolean = true, - ): ForItem? { - val forBlocks = getForDirectiveBlock(targetElement, skipSelf) - return getForItem(targetElement, forBlocks) - } - - fun getForItem( - targetElement: PsiElement, - forBlocks: List, - ): ForItem? { - val targetName = - targetElement.text - .replace("_has_next", "") - .replace("_index", "") - val forItem = forBlocks.lastOrNull { it.item.text == targetName } - forItem?.let { return ForItem(it.item) } ?: return null - } - - /** - * Analyze the field access of the for item definition and finally get the declared type - * @return [ValidationResult] is used to display the analysis results. - */ - fun validateFieldAccessByForItem( - blockElements: List, - skipSelf: Boolean = true, - ): ValidationResult? { - val targetElement: PsiElement = blockElements.firstOrNull() ?: return null - val topElm = blockElements.first() - - val forDirectives = getForDirectiveBlock(targetElement, skipSelf) - val forItem = getForItem(targetElement, forDirectives) ?: return createErrorResult(targetElement) - val domaAnnotationType = daoMethod.getDomaAnnotationType() - - val declarationItem = getDeclarationTopItem(forItem, 0) - if (declarationItem !is ForDeclarationDaoBaseItem) return createErrorResult(targetElement) - - val daoParamDeclarativeType = - declarationItem.getPsiParentClass() - ?: return ValidationDaoParamResult(targetElement, daoMethod.name, shortName) - - val initialType = daoParamDeclarativeType.type as? PsiClassType - if (initialType == null || !PsiClassTypeUtil.isIterableType(initialType, topElm.project)) return null - - val finalType = analyzeNestedForDirectives(forDirectives, initialType, domaAnnotationType.isBatchAnnotation(), topElm) - return finalType?.let { ValidationCompleteResult(topElm, PsiParentClass(it)) } ?: createErrorResult(targetElement) - } - - private fun createErrorResult(targetElement: PsiElement): ValidationResult = ValidationDaoParamResult(targetElement, "", shortName) - - private fun analyzeNestedForDirectives( - forDirectives: List, - initialType: PsiClassType, - isBatchAnnotation: Boolean, - topElm: PsiElement, - ): PsiClassType? { - var nestClassType: PsiClassType? = if (isBatchAnnotation) initialType.parameters.firstOrNull() as? PsiClassType else initialType - - // Follow the For directive and get the definition type of the current directive from the top definition type. - for ((i, targetForDirective) in forDirectives.withIndex()) { - if (nestClassType == null) break - val targetDirectiveParent = - PsiTreeUtil.getParentOfType( - targetForDirective.item, - SqlElForDirective::class.java, - ) - val targetElementParent = - PsiTreeUtil.getParentOfType( - topElm, - SqlElForDirective::class.java, - ) - - // Skip if the For directive in the search target is the same directory as the self. - if (targetDirectiveParent == targetElementParent) continue - - val currentForItem = ForItem(targetForDirective.item) - val currentDeclaration = currentForItem.getParentForDirectiveExpr()?.getForItemDeclaration() ?: continue - val declarationType = processDeclarationElement(currentDeclaration, nestClassType, i) - if (declarationType != null) { - if (!PsiClassTypeUtil.isIterableType(declarationType, topElm.project)) { - return null - } - nestClassType = declarationType.parameters.firstOrNull() as? PsiClassType - } else { - nestClassType = processListType(nestClassType, topElm) - } - } - return nestClassType - } - - /** - * Check the For directive definition element of the search target and get the declaration type - */ - private fun processDeclarationElement( - currentDeclaration: ForDeclarationItem, - nestClassType: PsiClassType, - index: Int, - ): PsiClassType? { - val declarationElement = currentDeclaration.element - if (declarationElement is SqlElStaticFieldAccessExpr) { - val forItem = ForDeclarationStaticFieldAccessorItem(declarationElement.accessElements, declarationElement, index) - val declarationType = forItem.getPsiParentClass()?.type as? PsiClassType - return declarationType - } - - val declarationChildren = currentDeclaration.getDeclarationChildren() - if (declarationChildren.size > 1) { - // Gets the type of reference property if it is defined with field access - val validator = SqlElForItemFieldAccessorChildElementValidator(declarationChildren, PsiParentClass(nestClassType), shortName) - return validator.validateChildren()?.parentClass?.type as? PsiClassType - } - return null - } - - /** - * Dig down the List definition type for the number of nesting levels - * from the top For directive definition type to obtain the type that applies to the current directive. - */ - private fun processListType( - nestClassType: PsiClassType, - topElm: PsiElement, - ): PsiClassType? { - var currentType: PsiClassType? = nestClassType - currentType?.let { type -> - if (PsiClassTypeUtil.isIterableType(type, topElm.project)) { - currentType = type.parameters.firstOrNull() as? PsiClassType - } - } - return currentType - } - - fun getForDirectiveBlockSize(target: PsiElement): Int = getForDirectiveBlock(target).size - - /** - * Count the `for`, `if`, and `end` elements from the beginning - * to the target element (`targetElement`) - * and obtain the `for` block information to which the `targetElement` belongs. - */ - private fun getForDirectiveBlock( - targetElement: PsiElement, - skipSelf: Boolean = true, - ): List { - val cachedValue = - cachedForDirectiveBlocks.getOrPut(targetElement) { - CachedValuesManager.getManager(targetElement.project).createCachedValue { - val file = targetElement.containingFile ?: return@createCachedValue null - val directiveBlocks = - PsiTreeUtil - .findChildrenOfType(file, PsiElement::class.java) - .filter { elm -> - ( - elm.elementType == SqlTypes.EL_FOR || - elm.elementType == SqlTypes.EL_IF || - elm.elementType == SqlTypes.EL_END - ) - }.sortedBy { it.textOffset } - .map { - when (it.elementType) { - SqlTypes.EL_FOR -> { - val item = (it.parent as? SqlElForDirective)?.getForItem() - BlockToken( - BlockType.FOR, - item ?: it, - item?.textOffset ?: 0, - ) - } - - SqlTypes.EL_IF -> BlockToken(BlockType.IF, it, it.textOffset) - else -> BlockToken(BlockType.END, it, it.textOffset) - } - } - val stack = mutableListOf() - val filterPosition = - if (skipSelf) { - directiveBlocks.filter { it.position < targetElement.textOffset } - } else { - directiveBlocks.filter { it.position <= targetElement.textOffset } - } - filterPosition.forEach { block -> - when (block.type) { - BlockType.FOR, BlockType.IF -> stack.add(block) - BlockType.END -> if (stack.isNotEmpty()) stack.removeAt(stack.lastIndex) - } - } - directiveBlocks.forEach { block -> - when (block.type) { - BlockType.FOR, BlockType.IF -> stack.add(block) - BlockType.END -> if (stack.isNotEmpty()) stack.removeAt(stack.lastIndex) - } - } - - CachedValueProvider.Result.create( - stack.filter { it.type == BlockType.FOR }, - file, - ) - } - } - return cachedValue.value - } - - /*** - * Get the top element to define the item. - */ - private fun getDeclarationTopItem( - forItem: ForItem, - searchIndex: Int = 0, - ): ForDirectiveItemBase? { - val forDirectiveParent = forItem.getParentForDirectiveExpr() ?: return null - val declarationSideElement = - forDirectiveParent.getForItemDeclaration() ?: return null - val declarationElement = declarationSideElement.element - if (declarationElement is SqlElStaticFieldAccessExpr) { - return ForDeclarationStaticFieldAccessorItem(declarationElement.accessElements, declarationElement, searchIndex) - } - - val declarationFieldElements = declarationSideElement.getDeclarationChildren().toMutableList() - val declarationSideElements = declarationFieldElements.firstOrNull() ?: return null - - val parentForItem = getForItem(declarationSideElements) - val index = searchIndex + 1 - if (parentForItem != null) { - val parentDeclaration = getDeclarationTopItem(parentForItem, index) - if (parentDeclaration is ForDeclarationDaoBaseItem) return parentDeclaration - } - - val validDaoParam = daoMethod.findParameter(declarationSideElements.text) - if (validDaoParam == null) return null - - return ForDeclarationDaoBaseItem( - declarationFieldElements, - validDaoParam, - searchIndex, - ) - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/quickfix/GenerateSqlQuickFix.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/quickfix/GenerateSqlQuickFix.kt index 96144be2..982dfa98 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/quickfix/GenerateSqlQuickFix.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/quickfix/GenerateSqlQuickFix.kt @@ -19,8 +19,8 @@ import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import org.domaframework.doma.intellij.bundle.MessageBundle -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil /** * Quick fix to generate SQL files with Dao methods that require SQL templates diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt index 610e2923..3f0177c8 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt @@ -19,24 +19,24 @@ import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiLiteralExpression +import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import org.domaframework.doma.intellij.common.dao.findDaoMethod import org.domaframework.doma.intellij.common.isInjectionSqlFile import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType -import org.domaframework.doma.intellij.common.sql.foritem.ForItem -import org.domaframework.doma.intellij.common.sql.validator.SqlElFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.common.sql.validator.SqlElForItemFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.common.sql.validator.SqlElStaticFieldAccessorChildElementValidator -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.sql.cleanString import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil import org.domaframework.doma.intellij.extension.expr.accessElements import org.domaframework.doma.intellij.extension.psi.findParameter import org.domaframework.doma.intellij.extension.psi.getForItem import org.domaframework.doma.intellij.extension.psi.isFirstElement -import org.domaframework.doma.intellij.inspection.ForDirectiveInspection +import org.domaframework.doma.intellij.extension.psi.psiClassType import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlElForDirective import org.domaframework.doma.intellij.psi.SqlElIdExpr @@ -90,36 +90,21 @@ class SqlInspectionVisitor( if (isLiteralOrStatic(element)) return PsiTreeUtil.getParentOfType(element, SqlElStaticFieldAccessExpr::class.java)?.let { return } - val daoMethod = findDaoMethod(visitFile) ?: return - - val forDirectiveInspection = - ForDirectiveInspection(daoMethod, this.shortName) + val forDirectiveExp = PsiTreeUtil.getParentOfType(element, SqlElForDirective::class.java) + if (forDirectiveExp != null && forDirectiveExp.getForItem() == element) return - if (PsiTreeUtil.getParentOfType(element, SqlElForDirective::class.java) != null) { - val currentForItem = ForItem(element) - val leftSideForItem = currentForItem.getParentForDirectiveExpr()?.getForItem() - if (leftSideForItem == element) return - - val forDirectivesSize = forDirectiveInspection.getForDirectiveBlockSize(element) - if (forDirectivesSize == 0) return - } - - // Element names defined in the For directory are not checked. - val forItem = forDirectiveInspection.getForItem(element) - if (forItem != null) { - return - } + val forItem = ForDirectiveUtil.findForItem(element) + if (forItem != null) return - val validDaoParam = daoMethod.findParameter(element.text) - if (validDaoParam == null) { - val validateResult = - ValidationDaoParamResult( - element, - daoMethod.name, - shortName, - ) - validateResult.highlightElement(holder) - } + val daoMethod = findDaoMethod(visitFile) ?: return + val param = daoMethod.findParameter(cleanString(element.text)) + if (param != null) return + + ValidationDaoParamResult( + element, + daoMethod.name, + this.shortName, + ).highlightElement(holder) } private fun getFieldAccessBlocks(element: SqlElFieldAccessExpr): List { @@ -136,33 +121,52 @@ class SqlInspectionVisitor( blockElement: List, file: PsiFile, ) { + val topElement = blockElement.firstOrNull() ?: return val daoMethod = findDaoMethod(file) ?: return - val forDirectiveInspection = ForDirectiveInspection(daoMethod, this.shortName) - var validateResult: ValidationResult? = - forDirectiveInspection.validateFieldAccessByForItem(blockElement.toList()) - if (validateResult is ValidationCompleteResult) { - val currentFieldAccessValidator = - SqlElForItemFieldAccessorChildElementValidator( - blockElement, - validateResult.parentClass, - this.shortName, - ) - validateResult = currentFieldAccessValidator.validateChildren() - if (validateResult is ValidationCompleteResult) return - } - if (validateResult is ValidationPropertyResult) { - validateResult.highlightElement(holder) - return - } - - val validator = - SqlElFieldAccessorChildElementValidator( + val project = topElement.project + val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(topElement) + val forItem = ForDirectiveUtil.findForItem(topElement, forDirectives = forDirectiveBlocks) + var isBatchAnnotation = false + val topElementParentClass = + if (forItem != null) { + val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) + if (result == null) { + errorHighlight(topElement, daoMethod, holder) + return + } + result + } else { + val paramType = daoMethod.findParameter(cleanString(topElement.text))?.type + if (paramType == null) { + errorHighlight(topElement, daoMethod, holder) + return + } + isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() + PsiParentClass(paramType) + } + + val result = + ForDirectiveUtil.getFieldAccessLastPropertyClassType( blockElement, - file, - this.shortName, + project, + topElementParentClass, + shortName = this.shortName, + isBatchAnnotation = isBatchAnnotation, ) - validateResult = validator.validateChildren() - validateResult?.highlightElement(holder) + + result?.highlightElement(holder) + } + + private fun errorHighlight( + topElement: SqlElIdExpr, + daoMethod: PsiMethod, + holder: ProblemsHolder, + ) { + ValidationDaoParamResult( + topElement, + daoMethod.name, + this.shortName, + ).highlightElement(holder) } /** @@ -173,13 +177,28 @@ class SqlInspectionVisitor( holder: ProblemsHolder, ) { val blockElements = staticAccuser.accessElements - val validator = - SqlElStaticFieldAccessorChildElementValidator( - blockElements, - staticAccuser, - this.shortName, - ) - val validateResult: ValidationResult? = validator.validateChildren() - validateResult?.highlightElement(holder) + val psiStaticClass = PsiStaticElement(staticAccuser.elClass.elIdExprList, staticAccuser.containingFile) + val referenceClass = psiStaticClass.getRefClazz() ?: return + val topParentClass = ForDirectiveUtil.getStaticFieldAccessTopElementClassType(staticAccuser, referenceClass) + if (topParentClass == null) { + blockElements.firstOrNull()?.let { + ValidationPropertyResult( + it, + PsiParentClass(referenceClass.psiClassType), + this.shortName, + ).highlightElement(holder) + } + return + } + val result = + topParentClass.let { + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + blockElements, + staticAccuser.project, + it, + shortName = this.shortName, + ) + } + result?.highlightElement(holder) } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoMethodRenameProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoMethodRenameProcessor.kt index 279ea477..e442ff0c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoMethodRenameProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoMethodRenameProcessor.kt @@ -20,8 +20,8 @@ import com.intellij.psi.PsiMethod import com.intellij.refactoring.listeners.RefactoringElementListener import com.intellij.refactoring.rename.RenameJavaMethodProcessor import com.intellij.usageView.UsageInfo -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil /** * Rename DAO method diff --git a/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoPackageRenameListenerProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoPackageRenameListenerProcessor.kt index d379b6da..4b75f41f 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoPackageRenameListenerProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoPackageRenameListenerProcessor.kt @@ -29,8 +29,8 @@ import com.intellij.refactoring.listeners.RefactoringElementListener import com.intellij.refactoring.listeners.RefactoringElementListenerProvider import com.intellij.util.IncorrectOperationException import org.domaframework.doma.intellij.common.CommonPathParameter.Companion.RESOURCES_META_INF_PATH -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.getDaoClass +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.extension.getResourceRoot import org.domaframework.doma.intellij.extension.getResourcesSQLFile import org.jetbrains.kotlin.idea.base.util.module diff --git a/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoRenameProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoRenameProcessor.kt index 02374024..55231aa0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoRenameProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/refactoring/dao/DaoRenameProcessor.kt @@ -19,8 +19,8 @@ import com.intellij.psi.PsiElement import com.intellij.refactoring.listeners.RefactoringElementListener import com.intellij.refactoring.rename.RenameJavaClassProcessor import com.intellij.usageView.UsageInfo -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.getDaoClass +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.extension.getContentRoot import org.domaframework.doma.intellij.extension.getPackagePathFromDaoPath import org.jetbrains.kotlin.idea.base.util.module diff --git a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElClassExprReference.kt b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElClassExprReference.kt index 476266d3..f4110637 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElClassExprReference.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElClassExprReference.kt @@ -17,8 +17,8 @@ package org.domaframework.doma.intellij.reference import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil class SqlElClassExprReference( element: PsiElement, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElForDirectiveIdExprReference.kt b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElForDirectiveIdExprReference.kt index 1b4954ba..1fd8c9c4 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElForDirectiveIdExprReference.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElForDirectiveIdExprReference.kt @@ -18,7 +18,7 @@ package org.domaframework.doma.intellij.reference import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.common.PluginLoggerUtil +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlElForDirective import org.domaframework.doma.intellij.psi.SqlElIdExpr diff --git a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt index a899c5a2..8e2970f0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt @@ -18,17 +18,18 @@ package org.domaframework.doma.intellij.reference import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType -import org.domaframework.doma.intellij.common.PluginLoggerUtil import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiParentClass import org.domaframework.doma.intellij.common.sql.cleanString -import org.domaframework.doma.intellij.common.sql.validator.SqlElForItemFieldAccessorChildElementValidator +import org.domaframework.doma.intellij.common.sql.foritem.ForItem import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil import org.domaframework.doma.intellij.extension.psi.findParameter -import org.domaframework.doma.intellij.extension.psi.getDomaAnnotationType -import org.domaframework.doma.intellij.extension.psi.getIterableClazz -import org.domaframework.doma.intellij.inspection.ForDirectiveInspection +import org.domaframework.doma.intellij.extension.psi.getForItem import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlTypes @@ -41,13 +42,13 @@ class SqlElIdExprReference( ): PsiElement? { val targetElements = getBlockCommentElements(element, SqlElFieldAccessExpr::class.java) if (targetElements.isEmpty()) return null - val topElm = targetElements.firstOrNull() as? PsiElement ?: return null - if (topElm.prevSibling.elementType == SqlTypes.AT_SIGN) return null - val daoMethod = findDaoMethod(file) ?: return null - val forDirectiveInspection = ForDirectiveInspection(daoMethod) - val forItem = forDirectiveInspection.getForItem(topElm) + + // Refers to an element defined in the for directive + val isSelfSkip = isSelfSkip(topElm) + val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(element, isSelfSkip) + val forItem = ForDirectiveUtil.findForItem(element, forDirectives = forDirectiveBlocks) if (forItem != null && element.textOffset == topElm.textOffset) { PluginLoggerUtil.countLogging( this::class.java.simpleName, @@ -55,51 +56,53 @@ class SqlElIdExprReference( "Reference", startTime, ) - return forItem.element + return forItem } - val validateResult = forDirectiveInspection.validateFieldAccessByForItem(targetElements) - var parentClass = (validateResult as? ValidationCompleteResult)?.parentClass - if (validateResult is ValidationCompleteResult && parentClass != null) { - val validator = - SqlElForItemFieldAccessorChildElementValidator( - targetElements, - parentClass, - ) - val targetReferenceClass = validator.validateChildren(1) - if (targetReferenceClass is ValidationCompleteResult) { - val searchText = targetElements.lastOrNull()?.let { cleanString(it.text) } ?: "" - val targetParent = targetReferenceClass.parentClass - val reference = - targetParent.findField(searchText) ?: targetParent.findMethod(searchText) - if (reference != null) { - PluginLoggerUtil.countLogging( - this::class.java.simpleName, - "ReferenceEntityProperty", - "Reference", - startTime, - ) - } - return reference - } + val fieldAccessExpr = PsiTreeUtil.getParentOfType(element, SqlElFieldAccessExpr::class.java) + if (fieldAccessExpr == null || element.textOffset == topElm.textOffset) { + val daoMethod = findDaoMethod(file) ?: return null + return getReferenceDaoMethodParameter( + daoMethod, + element, + startTime, + ) } - val topParam = daoMethod.findParameter(topElm.text) ?: return null - parentClass = topParam.getIterableClazz(daoMethod.getDomaAnnotationType()) + val tolElementForItem = + ForDirectiveUtil.getForDirectiveItemClassType(topElm.project, forDirectiveBlocks) + var isBatchAnnotation = false + var parentClass = + if (tolElementForItem != null) { + tolElementForItem + } else { + val daoMethod = findDaoMethod(file) ?: return null + val param = daoMethod.findParameter(topElm.text) ?: return null + isBatchAnnotation = PsiDaoMethod(topElm.project, daoMethod).daoType.isBatchAnnotation() + PsiParentClass(param.type) + } + val result = + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + targetElements, + topElm.project, + parentClass, + isBatchAnnotation = isBatchAnnotation, + shortName = "", + dropLastIndex = 1, + ) - val symbolElement = - when (element.textOffset) { - targetElements.first().textOffset -> - getReferenceDaoMethodParameter( - daoMethod, - element, - startTime, - ) + if (result is ValidationCompleteResult) { + val lastType = result.parentClass + return getReferenceEntity(lastType, element, startTime) + } - else -> getReferenceEntity(parentClass, targetElements, startTime) - } + return null + } - return symbolElement + private fun isSelfSkip(targetElement: PsiElement): Boolean { + val forItem = ForItem(targetElement) + val forDirectiveExpr = forItem.getParentForDirectiveExpr() + return !(forDirectiveExpr != null && forDirectiveExpr.getForItem()?.textOffset == targetElement.textOffset) } private fun getReferenceDaoMethodParameter( @@ -121,43 +124,20 @@ class SqlElIdExprReference( private fun getReferenceEntity( topParentClass: PsiParentClass, - targetElement: List, + targetElement: PsiElement, startTime: Long, ): PsiElement? { - val searchText = cleanString(targetElement.lastOrNull()?.text ?: "") - if (targetElement.size <= 2) { - val reference = - topParentClass.findField(searchText) ?: topParentClass.findMethod(searchText) - if (reference != null) { - PluginLoggerUtil.countLogging( - this::class.java.simpleName, - "ReferenceEntityProperty", - "Reference", - startTime, - ) - } - return reference - } - - val validator = - SqlElForItemFieldAccessorChildElementValidator( - targetElement.dropLast(1), - topParentClass, + val searchText = cleanString(targetElement.text) + val reference = + topParentClass.findField(searchText) ?: topParentClass.findMethod(searchText) + if (reference != null) { + PluginLoggerUtil.countLogging( + this::class.java.simpleName, + "ReferenceEntityProperty", + "Reference", + startTime, ) - val validateResult = validator.validateChildren() - if (validateResult != null) { - val targetClass = validateResult.parentClass ?: return null - val reference = targetClass.findField(searchText) ?: targetClass.findMethod(searchText) - if (reference != null) { - PluginLoggerUtil.countLogging( - this::class.java.simpleName, - "ReferenceEntityProperty", - "Reference", - startTime, - ) - } - return reference } - return null + return reference } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElStaticFieldReference.kt b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElStaticFieldReference.kt index 54ee9f91..816151d6 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElStaticFieldReference.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElStaticFieldReference.kt @@ -18,9 +18,12 @@ package org.domaframework.doma.intellij.reference import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.common.PluginLoggerUtil -import org.domaframework.doma.intellij.common.sql.validator.SqlElStaticFieldAccessorChildElementValidator +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiStaticElement import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil +import org.domaframework.doma.intellij.extension.psi.psiClassType import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr class SqlElStaticFieldReference( @@ -36,25 +39,36 @@ class SqlElStaticFieldReference( val targetElements = getBlockCommentElements(element, SqlElStaticFieldAccessExpr::class.java) - val validator = - SqlElStaticFieldAccessorChildElementValidator( - targetElements, - staticAccessParent, - ) - val initialPsiParentClass = validator.getClassType() ?: return null - val project = file.project - val fieldAccessLastParentResult = - if (targetElements.size == 1) { - validator.validateFieldAccess(project, initialPsiParentClass, dropLastIndex = 1) - } else if (targetElements.size >= 2) { - validator.validateChildren(1) - } else { - null + val psiStaticClass = PsiStaticElement(staticAccessParent.elClass.elIdExprList, staticAccessParent.containingFile) + val referenceClass = psiStaticClass.getRefClazz() ?: return null + val referenceParentClass = PsiParentClass(referenceClass.psiClassType) + if (targetElements.size == 1) { + val searchText = element.text ?: "" + val reference = referenceParentClass.findField(searchText) ?: referenceParentClass.findMethod(searchText) + if (reference != null) { + PluginLoggerUtil.countLogging( + this::class.java.simpleName, + "ReferenceStaticProperty", + "Reference", + startTime, + ) + } + return reference + } + val topFieldClassType = ForDirectiveUtil.getStaticFieldAccessTopElementClassType(staticAccessParent, referenceClass) + val result = + topFieldClassType?.let { + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + targetElements, + staticAccessParent.project, + it, + dropLastIndex = 1, + ) } - if (fieldAccessLastParentResult is ValidationCompleteResult) { + if (result is ValidationCompleteResult) { + val parent = result.parentClass val searchText = element.text ?: "" - val parent = fieldAccessLastParentResult.parentClass val reference = parent.findField(searchText) ?: parent.findMethod(searchText) if (reference != null) { PluginLoggerUtil.countLogging( @@ -66,7 +80,6 @@ class SqlElStaticFieldReference( } return reference } - return null } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/setting/DomaToolStartupActivity.kt b/src/main/kotlin/org/domaframework/doma/intellij/setting/DomaToolStartupActivity.kt index af1693b5..3c9cb78a 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/setting/DomaToolStartupActivity.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/setting/DomaToolStartupActivity.kt @@ -18,7 +18,7 @@ package org.domaframework.doma.intellij.setting import com.intellij.openapi.application.PathManager import com.intellij.openapi.project.Project import com.intellij.openapi.startup.ProjectActivity -import org.domaframework.doma.intellij.common.PluginUtil +import org.domaframework.doma.intellij.common.util.PluginUtil class DomaToolStartupActivity : ProjectActivity { override suspend fun execute(project: Project) { diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml index 2ae44a63..c3778232 100644 --- a/src/main/resources/logback-test.xml +++ b/src/main/resources/logback-test.xml @@ -29,7 +29,7 @@ - + \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 2ae44a63..c3778232 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -29,7 +29,7 @@ - + \ No newline at end of file diff --git a/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt index 7afa72c2..3df983e4 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt @@ -54,6 +54,7 @@ open class DomaSqlTest : LightJavaCodeInsightFixtureTestCase() { addEntityJavaFile("EmployeeSummary.java") addEntityJavaFile("Project.java") addEntityJavaFile("ProjectDetail.java") + addEntityJavaFile("Principal.java") } @Throws(Exception::class) diff --git a/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt b/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt index 74097a71..3b712074 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt @@ -33,6 +33,8 @@ class SqlSymbolDocumentTestCase : DomaSqlTest() { addSqlFile("$testPackage/$testDaoName/documentForItemElementInBindVariable.sql") addSqlFile("$testPackage/$testDaoName/documentForItemElementInIfDirective.sql") addSqlFile("$testPackage/$testDaoName/documentForItemElementByFieldAccess.sql") + addSqlFile("$testPackage/$testDaoName/documentForItemFirstElement.sql") + addSqlFile("$testPackage/$testDaoName/documentForItemStaticProperty.sql") } fun testDocumentForItemDaoParam() { @@ -86,6 +88,22 @@ class SqlSymbolDocumentTestCase : DomaSqlTest() { documentationTest(sqlName, result) } + fun testDocumentForItemFirstElement() { + val sqlName = "documentForItemFirstElement" + val result = + "Permission item" + + documentationFindTextTest(sqlName, "item", result) + } + + fun testDocumentForItemStaticProperty() { + val sqlName = "documentForItemStaticProperty" + val result = + "Project project" + + documentationFindTextTest(sqlName, "project", result) + } + private fun documentationTest( sqlName: String, result: String?, diff --git a/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java b/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java index 12572374..d648cd3a 100644 --- a/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java @@ -26,4 +26,10 @@ public interface DocumentTestDao { @Select List documentForItemElementByFieldAccess(List> employeesList); + @Select + int documentForItemFirstElement(Principal principal); + + @Select + int documentForItemStaticProperty(); + } \ No newline at end of file diff --git a/src/test/testData/src/main/java/doma/example/entity/Employee.java b/src/test/testData/src/main/java/doma/example/entity/Employee.java index 5b62c748..a9af3501 100644 --- a/src/test/testData/src/main/java/doma/example/entity/Employee.java +++ b/src/test/testData/src/main/java/doma/example/entity/Employee.java @@ -14,7 +14,7 @@ public class Employee extends User { public String employeeName; private String department; private String rank; - public static List projects; + public static List projects; public Integer managerId; diff --git a/src/test/testData/src/main/java/doma/example/entity/Principal.java b/src/test/testData/src/main/java/doma/example/entity/Principal.java new file mode 100644 index 00000000..12fdda73 --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/entity/Principal.java @@ -0,0 +1,13 @@ +package doma.example.entity; + +import java.util.List; + +public class Principal { + + private List permissions; + + public static class Permission { + public String name; + public String description; + } +} \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeParameterFirstProperty.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeParameterFirstProperty.sql index c793ac7a..0cb77bdc 100644 --- a/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeParameterFirstProperty.sql +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeParameterFirstProperty.sql @@ -1 +1 @@ -select * from employee where id = /* employee.department.startWith(employee.) */1 \ No newline at end of file +select * from employee where id = /* employee.department.startsWith(employee. ) */1 \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemFirstElement.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemFirstElement.sql new file mode 100644 index 00000000..d933edb9 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemFirstElement.sql @@ -0,0 +1,8 @@ +select count(*) from principal +where +/*%for item : principal.permissions */ + name = /* item.name */'name' + /*%if item_has_next */ + /*# "or" */ + /*%end */ +/*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemStaticProperty.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemStaticProperty.sql new file mode 100644 index 00000000..d084122d --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemStaticProperty.sql @@ -0,0 +1,8 @@ +select count(*) from principal +where +/*%for project : @doma.example.entity.Employee@projects */ + name = /* project.projectNumber */'000' + /*%if project_has_next */ + /*# "or" */ + /*%end */ +/*%end */ \ No newline at end of file