Coverage Summary for Class: KronosParserTransformer (com.kotlinorm.compiler.plugin.transformer)

Class Class, % Method, % Branch, % Line, % Instruction, %
KronosParserTransformer 100% (1/1) 100% (14/14) 85.4% (35/41) 100% (85/85) 99.3% (577/581)


 /**
  * Copyright 2022-2025 kronos-orm
  *
  * 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
  *
  *     http://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 com.kotlinorm.compiler.plugin.transformer
 
 import com.kotlinorm.compiler.plugin.transformer.kTable.KTableParserForConditionTransformer
 import com.kotlinorm.compiler.plugin.transformer.kTable.KTableParserForReferenceTransformer
 import com.kotlinorm.compiler.plugin.transformer.kTable.KTableParserForSelectTransformer
 import com.kotlinorm.compiler.plugin.transformer.kTable.KTableParserForSetTransformer
 import com.kotlinorm.compiler.plugin.transformer.kTable.KTableParserForSortReturnTransformer
 import com.kotlinorm.compiler.plugin.utils.KClassCreatorUtil.initFunctions
 import com.kotlinorm.compiler.plugin.utils.KClassCreatorUtil.kPojoClasses
 import com.kotlinorm.compiler.plugin.utils.KPojoFqName
 import com.kotlinorm.compiler.plugin.utils.context.KotlinBuilderContext
 import com.kotlinorm.compiler.plugin.utils.context.withBuilder
 import com.kotlinorm.compiler.plugin.utils.fqNameOfSelectFromsRegexes
 import com.kotlinorm.compiler.plugin.utils.fqNameOfTypedQuery
 import com.kotlinorm.compiler.plugin.utils.kTableForCondition.KTABLE_FOR_CONDITION_CLASS
 import com.kotlinorm.compiler.plugin.utils.kTableForReference.KTABLE_FOR_REFERENCE_CLASS
 import com.kotlinorm.compiler.plugin.utils.kTableForSelect.KTABLE_FOR_SELECT_CLASS
 import com.kotlinorm.compiler.plugin.utils.kTableForSet.KTABLE_FOR_SET_CLASS
 import com.kotlinorm.compiler.plugin.utils.kTableForSort.KTABLE_FOR_SORT_CLASS
 import com.kotlinorm.compiler.plugin.utils.updateTypedQueryParameters
 import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
 import org.jetbrains.kotlin.ir.IrStatement
 import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
 import org.jetbrains.kotlin.ir.builders.irBlock
 import org.jetbrains.kotlin.ir.builders.irBlockBody
 import org.jetbrains.kotlin.ir.declarations.IrClass
 import org.jetbrains.kotlin.ir.declarations.IrFunction
 import org.jetbrains.kotlin.ir.expressions.IrBlockBody
 import org.jetbrains.kotlin.ir.expressions.IrCall
 import org.jetbrains.kotlin.ir.expressions.IrClassReference
 import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrExpression
 import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
 import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
 import org.jetbrains.kotlin.ir.types.classFqName
 import org.jetbrains.kotlin.ir.types.classOrNull
 import org.jetbrains.kotlin.ir.types.getClass
 import org.jetbrains.kotlin.ir.util.hasAnnotation
 import org.jetbrains.kotlin.ir.util.kotlinFqName
 import org.jetbrains.kotlin.ir.util.statements
 import org.jetbrains.kotlin.ir.util.superTypes
 import org.jetbrains.kotlin.name.FqName
 
 /**
  * Kronos Parser Transformer
  *
  * @author OUSC, Jieyao Lu
  */
 class KronosParserTransformer(
     private val pluginContext: IrPluginContext
 ) : IrElementTransformerVoidWithContext() {
     private var initAnnotationFqName = FqName("com.kotlinorm.annotations.KronosInit")
 
     @OptIn(UnsafeDuringIrConstructionAPI::class)
     override fun visitCall(expression: IrCall): IrExpression {
         with(pluginContext) {
             for(i in 0 until expression.typeArgumentsCount) {
                 val typeArgument = expression.getTypeArgument(i)
                 if (typeArgument != null && typeArgument.getClass() != null && typeArgument.superTypes().any { it.classFqName == KPojoFqName }) {
                     kPojoClasses.add(typeArgument.getClass()!!)
                 }
             }
             if (expression.symbol.owner.hasAnnotation(initAnnotationFqName)) {
                 val initializer = (expression.getValueArgument(0) as IrFunctionExpressionImpl).function
                 with(DeclarationIrBuilder(pluginContext, initializer.symbol) as IrBuilderWithScope) {
                     initFunctions.add(
                         KotlinBuilderContext(
                             pluginContext,
                             this
                         ) to initializer
                     )
                 }
             }
             return transformKQueryTask(expression) {
                 super.visitCall(expression)
             }
         }
     }
     /**
      * Visits a new function and performs different actions based on the extension receiver's return type.
      *
      * @param declaration the [IrFunction] being visited
      * @return the transformed function body or the result of calling the super class's implementation
      */
     override fun visitFunctionNew(declaration: IrFunction): IrStatement {
         when (declaration.extensionReceiverParameter?.type?.classFqName?.asString()) {
             KTABLE_FOR_SELECT_CLASS -> declaration.body = transformKTableForSelect(declaration)
             KTABLE_FOR_SET_CLASS -> declaration.body = transformKTableForSet(declaration)
             KTABLE_FOR_CONDITION_CLASS -> declaration.body = transformKTableForCondition(declaration)
             KTABLE_FOR_SORT_CLASS -> declaration.body = transformKTableForSort(declaration)
             KTABLE_FOR_REFERENCE_CLASS -> declaration.body = transformKTableForReference(declaration)
         }
         return super.visitFunctionNew(declaration)
     }
 
     /**
      * Visits a new class declaration and performs transformation if it is a subclass of "com.kotlinorm.interfaces.KPojo".
      *
      * @param declaration the class declaration to visit
      * @return the transformed class declaration or the result of calling the super class's implementation
      */
     override fun visitClassNew(declaration: IrClass): IrStatement {
         with(pluginContext) {
             if (declaration.superTypes.any { it.classFqName == KPojoFqName }) {
                 kPojoClasses.add(declaration)
                 return super.visitClassNew(declaration)
                     .transform(KronosIrClassNewTransformer(pluginContext, declaration), null) as IrStatement
             }
         }
         return super.visitClassNew(declaration)
     }
 
     @OptIn(UnsafeDuringIrConstructionAPI::class)
     override fun visitClassReference(expression: IrClassReference): IrExpression {
         with(pluginContext) {
             val declaration = expression.classType.classOrNull?.owner
             if (declaration != null && declaration.superTypes.any { it.classFqName == KPojoFqName }) {
                 kPojoClasses.add(declaration)
             }
         }
         return super.visitClassReference(expression)
     }
 
     @OptIn(UnsafeDuringIrConstructionAPI::class)
     override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
         with(pluginContext) {
             val declaration = expression.type.classOrNull?.owner
             if (declaration != null && declaration.superTypes.any { it.classFqName == KPojoFqName }) {
                 kPojoClasses.add(declaration)
             }
         }
         return super.visitConstructorCall(expression)
     }
 
     /**
      * Transforms the given IrFunction representing a ktable declaration into an IrBlockBody.
      *
      * @param irFunction the [IrFunction] to be transformed
      * @return the transformed IrBlockBody representing the ktable declaration
      */
     private fun transformKTableForSelect(
         irFunction: IrFunction
     ): IrBlockBody {
         return DeclarationIrBuilder(pluginContext, irFunction.symbol).irBlockBody {
             +irBlock {
                 +irFunction.body!!.statements
             }
                 .transform(KTableParserForSelectTransformer(pluginContext, irFunction), null)
         }
     }
 
     /**
      * Transforms the given IrFunction representing a ktable declaration into an IrBlockBody.
      *
      * @param irFunction the [IrFunction] to be transformed
      * @return the transformed IrBlockBody representing the ktable declaration
      */
     private fun transformKTableForSet(
         irFunction: IrFunction
     ): IrBlockBody {
         return DeclarationIrBuilder(pluginContext, irFunction.symbol).irBlockBody {
             +irBlock {
                 +irFunction.body!!.statements
             }
                 .transform(KTableParserForSetTransformer(pluginContext, irFunction), null)
         }
     }
 
     /**
      * Transforms the given IrFunction representing a ktable conditional declaration into an IrBlockBody.
      *
      * @param irFunction the [IrFunction] to be transformed
      * @return the transformed IrBlockBody representing the ktable conditional declaration
      */
     private fun transformKTableForCondition(
         irFunction: IrFunction
     ): IrBlockBody {
         return DeclarationIrBuilder(pluginContext, irFunction.symbol).irBlockBody {
             +irBlock(resultType = irFunction.returnType) {
                 +irFunction.body!!.statements
             }.transform(KTableParserForConditionTransformer(pluginContext, irFunction), null)
         }
     }
 
     /**
      * Transforms the given IrFunction representing a ktable sortable declaration into an IrBlockBody.
      *
      * @param irFunction the IrFunction to be transformed
      * @return the transformed IrBlockBody representing the ktable sortable declaration
      */
     private fun transformKTableForSort(
         irFunction: IrFunction
     ): IrBlockBody {
         return DeclarationIrBuilder(pluginContext, irFunction.symbol).irBlockBody {
             +irBlock(resultType = irFunction.returnType) {
                 +irFunction.body!!.statements
             }
                 .transform(KTableParserForSortReturnTransformer(pluginContext, irFunction), null)
         }
     }
 
     /**
      * Transforms the given IrFunction representing a ktable reference declaration into an IrBlockBody.
      *
      * @param irFunction the IrFunction to be transformed
      * @return the transformed IrBlockBody representing the ktable reference declaration
      */
     private fun transformKTableForReference(
         irFunction: IrFunction
     ): IrBlockBody {
         return DeclarationIrBuilder(pluginContext, irFunction.symbol).irBlockBody {
             +irBlock(resultType = irFunction.returnType) {
                 +irFunction.body!!.statements
             }
                 .transform(KTableParserForReferenceTransformer(pluginContext, irFunction), null)
         }
     }
 
     @OptIn(UnsafeDuringIrConstructionAPI::class)
     private fun transformKQueryTask(expression: IrCall, finally: () -> IrExpression): IrExpression {
         val fqNameOfIrCall = expression.symbol.owner.kotlinFqName
         if (
             (fqNameOfIrCall in fqNameOfTypedQuery ||
             fqNameOfSelectFromsRegexes.any { Regex(it).matches(fqNameOfIrCall.asString()) }) &&
             expression.typeArgumentsCount == 1
         ) {
             return DeclarationIrBuilder(pluginContext, expression.symbol).irBlock {
                 withBuilder(pluginContext) {
                     +updateTypedQueryParameters(expression)
                 }
                 finally()
             }
         }
         return finally()
     }
 }