From 83a82401e87fd8147842d4f4f40ea6fd780bdc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yanneck=20Rei=C3=9F?= Date: Mon, 19 Jun 2023 12:33:28 +0200 Subject: [PATCH] Introduced configuration parameters - Added configuration parameter to suppress warnings about mapping mismatches - Updated to Kotlin 1.8.21 - Upgraded Gradle to version 8.0.0 - Set compile target to JDK 17 - Updated jitpack config to use openJDK 17 --- README.MD | 21 ++++++-- build.gradle.kts | 8 +-- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- jitpack.yml | 2 +- .../kconmapper/annotations/KConMapper.kt | 2 + .../annotations/KConMapperProperty.kt | 2 + .../kconmapper/KCMSymbolProcessorProvider.kt | 4 +- .../processor/KCMSymbolProcessor.kt | 21 ++++++-- .../common/KConMapperConfiguration.kt | 11 ++++ .../generator/MappingFunctionGenerator.kt | 50 ++++++++++++++----- .../processor/visitor/KCMVisitor.kt | 10 ++-- sample/build.gradle.kts | 14 +++++- .../generictypeexample/cat/CatCareStation.kt | 2 +- 14 files changed, 117 insertions(+), 36 deletions(-) create mode 100644 kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/common/KConMapperConfiguration.kt diff --git a/README.MD b/README.MD index 1f6ac83..4b074da 100644 --- a/README.MD +++ b/README.MD @@ -153,6 +153,19 @@ data class CreateExampleDTO( With the `@KConMapperProperty` annotation in place, the `exampleMapper` function will map the fullName variable in the CreateExampleDTO class to the name variable in the ExampleEntity class and the yearsOld variable to the age variable when it is called. +--- +## Configurations + +You can adapt the configuration of KConMapper to your needs. + +To suppress outputted warnings about missing variable mappings, you can add the following to your `build.gradle` file: + +```kotlin +ksp { + arg("kconmapper.suppressMappingMismatchWarnings", "true") +} +``` + --- ## Setup @@ -199,8 +212,8 @@ repositories { dependencies { // .. - implementation 'com.github.yanneckreiss.kconmapper:annotations:1.0.0-alpha05' - ksp 'com.github.yanneckreiss.kconmapper:ksp:1.0.0-alpha05' + implementation 'com.github.yanneckreiss.kconmapper:annotations:1.0.0-alpha06' + ksp 'com.github.yanneckreiss.kconmapper:ksp:1.0.0-alpha06' } ``` @@ -215,8 +228,8 @@ repositories { dependencies { // .. - implementation("com.github.yanneckreiss.kconmapper:annotations:1.0.0-alpha05") - ksp("com.github.yanneckreiss.kconmapper:ksp:1.0.0-alpha05") + implementation("com.github.yanneckreiss.kconmapper:annotations:1.0.0-alpha06") + ksp("com.github.yanneckreiss.kconmapper:ksp:1.0.0-alpha06") } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 5eb5427..15eec0e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile allprojects { - version = "1.0.0-alpha05" + version = "1.0.0-alpha06" group = "com.github.yanneckreiss.kconmapper" repositories { @@ -39,8 +39,8 @@ subprojects { } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 withSourcesJar() withJavadocJar() @@ -59,7 +59,7 @@ subprojects { tasks.withType { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "11" + jvmTarget = "17" } } } diff --git a/gradle.properties b/gradle.properties index 17fe40d..f4fc8df 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ -kotlinVersion=1.8.0 -kspVersion=1.8.0-1.0.8 +kotlinVersion=1.8.21 +kspVersion=1.8.21-1.0.11 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..da1db5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/jitpack.yml b/jitpack.yml index adb3fe1..efde7bf 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,2 @@ jdk: - - openjdk11 + - openjdk17 diff --git a/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapper.kt b/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapper.kt index 54a53e1..dade752 100644 --- a/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapper.kt +++ b/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapper.kt @@ -9,6 +9,8 @@ import kotlin.reflect.KClass * @param fromClasses define classes you want to map from to the annotated class. * The result will be `ClassFromAnnotationParameter.toKConMapperAnnotatedClass(): KConMapperAnnotatedClass` */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.SOURCE) annotation class KConMapper( val toClasses: Array> = [], val fromClasses: Array> = [] diff --git a/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapperProperty.kt b/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapperProperty.kt index 4531736..5aab63e 100644 --- a/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapperProperty.kt +++ b/kconmapper-annotations/src/main/java/com/github/yanneckreiss/kconmapper/annotations/KConMapperProperty.kt @@ -7,4 +7,6 @@ package com.github.yanneckreiss.kconmapper.annotations * Useful if the target class has a different name than the * property of the sourceClass class but describes the same property. */ +@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.SOURCE) annotation class KConMapperProperty(val aliases: Array) diff --git a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/KCMSymbolProcessorProvider.kt b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/KCMSymbolProcessorProvider.kt index ebd54b8..f9b5118 100644 --- a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/KCMSymbolProcessorProvider.kt +++ b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/KCMSymbolProcessorProvider.kt @@ -4,13 +4,15 @@ import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.processing.SymbolProcessorProvider import de.yanneckreiss.kconmapper.processor.KCMSymbolProcessor +import de.yanneckreiss.kconmapper.processor.common.KConMapperConfiguration class KCMSymbolProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { return KCMSymbolProcessor( codeGenerator = environment.codeGenerator, - logger = environment.logger + logger = environment.logger, + kConMapperConfiguration = KConMapperConfiguration(environment.options) ) } } diff --git a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/KCMSymbolProcessor.kt b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/KCMSymbolProcessor.kt index 231c1b1..6849a10 100644 --- a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/KCMSymbolProcessor.kt +++ b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/KCMSymbolProcessor.kt @@ -8,6 +8,7 @@ import com.google.devtools.ksp.symbol.ClassKind import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.validate +import de.yanneckreiss.kconmapper.processor.common.KConMapperConfiguration import de.yanneckreiss.kconmapper.processor.visitor.KCMVisitor private const val KCONMAPPER_PACKAGE_NAME = "com.github.yanneckreiss.kconmapper" @@ -19,12 +20,15 @@ const val KCONMAPPER_ANNOTATION_NAME = "KConMapper" */ class KCMSymbolProcessor( val codeGenerator: CodeGenerator, - val logger: KSPLogger + val logger: KSPLogger, + val kConMapperConfiguration: KConMapperConfiguration ) : SymbolProcessor { override fun process(resolver: Resolver): List { - val annotationPackagePath = "$KCONMAPPER_PACKAGE_NAME.$KCONMAPPER_ANNOTATIONS_PACKAGE_NAME.$KCONMAPPER_ANNOTATION_NAME" - val resolvedSymbols: Sequence = resolver.getSymbolsWithAnnotation(annotationName = annotationPackagePath) + val annotationPackagePath = + "$KCONMAPPER_PACKAGE_NAME.$KCONMAPPER_ANNOTATIONS_PACKAGE_NAME.$KCONMAPPER_ANNOTATION_NAME" + val resolvedSymbols: Sequence = + resolver.getSymbolsWithAnnotation(annotationName = annotationPackagePath) resolvedSymbols .filter { ksAnnotated -> ksAnnotated is KSClassDeclaration && ksAnnotated.validate() } @@ -44,8 +48,17 @@ class KCMSymbolProcessor( } else -> { + val kcmVisitor = KCMVisitor( + codeGenerator = codeGenerator, + resolver = resolver, + logger = logger, + configuration = kConMapperConfiguration, + ) // Class type is supported - ksAnnotated.accept(KCMVisitor(codeGenerator, resolver, logger), Unit) + ksAnnotated.accept( + visitor = kcmVisitor, + data = Unit + ) } } } diff --git a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/common/KConMapperConfiguration.kt b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/common/KConMapperConfiguration.kt new file mode 100644 index 0000000..1adc964 --- /dev/null +++ b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/common/KConMapperConfiguration.kt @@ -0,0 +1,11 @@ +package de.yanneckreiss.kconmapper.processor.common + +private const val OPTION_SUPPRESS_MAPPING_MISMATCH_WARNINGS = "kconmapper.suppressMappingMismatchWarnings" + +data class KConMapperConfiguration( + val suppressMappingMismatchWarnings: Boolean +) { + constructor(options: Map) : this( + suppressMappingMismatchWarnings = options[OPTION_SUPPRESS_MAPPING_MISMATCH_WARNINGS]?.toBoolean() ?: false + ) +} diff --git a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/generator/MappingFunctionGenerator.kt b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/generator/MappingFunctionGenerator.kt index c2ec9c6..33d7398 100644 --- a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/generator/MappingFunctionGenerator.kt +++ b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/generator/MappingFunctionGenerator.kt @@ -4,6 +4,7 @@ import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.symbol.* import de.yanneckreiss.kconmapper.processor.* +import de.yanneckreiss.kconmapper.processor.common.KConMapperConfiguration import de.yanneckreiss.kconmapper.processor.generator.argument.ArgumentType import de.yanneckreiss.kconmapper.processor.generator.argument.MatchingArgument import de.yanneckreiss.kconmapper.processor.visitor.PackageImports @@ -35,7 +36,8 @@ class MappingFunctionGenerator( fun generateMappingFunction( sourceClass: KSClassDeclaration, targetClass: KSClassDeclaration, - packageImports: PackageImports + packageImports: PackageImports, + configuration: KConMapperConfiguration ): String { val targetClassName: String = targetClass.simpleName.getShortName() @@ -56,7 +58,8 @@ class MappingFunctionGenerator( targetClass = targetClass, targetClassTypeParameters = targetClassTypeParameters, targetClassName = targetClassName, - packageImports = packageImports + packageImports = packageImports, + configuration = configuration ) } @@ -65,7 +68,8 @@ class MappingFunctionGenerator( targetClass: KSClassDeclaration, targetClassTypeParameters: List, targetClassName: String, - packageImports: PackageImports + packageImports: PackageImports, + configuration: KConMapperConfiguration ): String { var extensionFunctions = "" @@ -81,14 +85,15 @@ class MappingFunctionGenerator( targetClassTypeParameters = targetClassTypeParameters, packageImports = packageImports, sourceClassName = sourceClassName, - targetClassName = targetClassName + targetClassName = targetClassName, + configuration = configuration ) // Add missing arguments to function head if (missingConstructorArguments.isNotEmpty()) { // Warn the user if unnecessary KConMapper got declared - if (matchingConstructorArguments.isEmpty()) { + if (matchingConstructorArguments.isEmpty() && !configuration.suppressMappingMismatchWarnings) { logger.warn( message = "\n\nNo matching arguments for mapping from class `$sourceClassName` to `$targetClassName`. \n" + "You can instead remove the argument `$sourceClassName::class` from the `${KCONMAPPER_ANNOTATION_NAME}` annotation in class `$targetClassName` " + @@ -101,7 +106,10 @@ class MappingFunctionGenerator( if (targetClassTypeParameters.isNotEmpty()) { extensionFunctions += "$KOTLIN_FUNCTION_KEYWORD $DIAMOND_OPERATOR_OPEN" targetClassTypeParameters.forEachIndexed { index, targetClassTypeParameter: KSTypeParameter -> - val separator: String = getArgumentDeclarationLineEnding(hasNextLine = targetClassTypeParameters.lastIndex != index, addSpace = true) + val separator: String = getArgumentDeclarationLineEnding( + hasNextLine = targetClassTypeParameters.lastIndex != index, + addSpace = true + ) extensionFunctions += targetClassTypeParameter.name.asString() // TODO: Handle multiple upper bounds @@ -113,9 +121,21 @@ class MappingFunctionGenerator( extensionFunctions += separator } - extensionFunctions += "$DIAMOND_OPERATOR_CLOSE ${generateExtensionFunctionName(sourceClass, targetClass, packageImports)}(\n" + extensionFunctions += "$DIAMOND_OPERATOR_CLOSE ${ + generateExtensionFunctionName( + sourceClass, + targetClass, + packageImports + ) + }(\n" } else { - extensionFunctions += "$KOTLIN_FUNCTION_KEYWORD ${generateExtensionFunctionName(sourceClass, targetClass, packageImports)}(\n" + extensionFunctions += "$KOTLIN_FUNCTION_KEYWORD ${ + generateExtensionFunctionName( + sourceClass, + targetClass, + packageImports + ) + }(\n" } missingConstructorArguments.forEachIndexed { missingArgumentIndex: Int, missingArgument: KSValueParameter -> @@ -161,7 +181,8 @@ class MappingFunctionGenerator( // Assign matching values from source class to target class constructor matchingConstructorArguments.forEachIndexed { index: Int, matchingArgument: MatchingArgument -> - val lineEnding: String = getArgumentDeclarationLineEnding(hasNextLine = missingConstructorArguments.lastIndex != index || missingConstructorArguments.isNotEmpty()) + val lineEnding: String = + getArgumentDeclarationLineEnding(hasNextLine = missingConstructorArguments.lastIndex != index || missingConstructorArguments.isNotEmpty()) extensionFunctions += "\t${matchingArgument.targetClassPropertyName} = this.${matchingArgument.sourceClassPropertyName}" // If source class is a generic type parameter we need to cast it to avoid compilation errors @@ -174,7 +195,8 @@ class MappingFunctionGenerator( // Assign values from function head to constructor param missingConstructorArguments.forEachIndexed { index: Int, paramName: KSValueParameter -> - val lineEnding: String = getArgumentDeclarationLineEnding(hasNextLine = missingConstructorArguments.lastIndex != index) + val lineEnding: String = + getArgumentDeclarationLineEnding(hasNextLine = missingConstructorArguments.lastIndex != index) extensionFunctions += "\t$paramName = $paramName$lineEnding\n" } @@ -255,7 +277,8 @@ class MappingFunctionGenerator( targetClassTypeParameters: List, packageImports: PackageImports, sourceClassName: String, - targetClassName: String + targetClassName: String, + configuration: KConMapperConfiguration ): Pair, MutableList> { val missingArguments = mutableListOf() @@ -271,7 +294,8 @@ class MappingFunctionGenerator( val parameterNameFromSourceClass: String = parameterFromSourceClass.simpleName.asString() // Get the aliases from the KConMapperProperty annotation, can in reality only be of type ArrayList? - val aliases: Set = findKConMapperPropertyAliases(parameterFromSourceClass.annotations + valueParam.annotations) + val aliases: Set = + findKConMapperPropertyAliases(parameterFromSourceClass.annotations + valueParam.annotations) // The argument matches if either the actual name or the alias from the KConMapperProperty annotation is the same if ((parameterNameFromSourceClass == valueName) || aliases.any { alias -> alias == valueName || alias == parameterNameFromSourceClass }) { @@ -326,7 +350,7 @@ class MappingFunctionGenerator( typeParam.simpleName.asString() + parameterTypeFromTargetClass.markedNullableAsString() } ) - } else { + } else if (!configuration.suppressMappingMismatchWarnings) { logger.warn( message = "Found matching parameter from class `$sourceClassName` for property `$valueName` of " + "targetClass `$targetClassName` but the type `${parameterTypeFromSourceClass}` " + diff --git a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/visitor/KCMVisitor.kt b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/visitor/KCMVisitor.kt index 15f39dc..642c063 100644 --- a/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/visitor/KCMVisitor.kt +++ b/kconmapper-ksp/src/main/kotlin/de/yanneckreiss/kconmapper/processor/visitor/KCMVisitor.kt @@ -6,6 +6,7 @@ import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.symbol.* import de.yanneckreiss.kconmapper.processor.* +import de.yanneckreiss.kconmapper.processor.common.KConMapperConfiguration import de.yanneckreiss.kconmapper.processor.generator.MappingFunctionGenerator import java.io.OutputStream @@ -35,7 +36,8 @@ private const val GENERATED_FILE_PATH = "de.yanneckreiss.kconmapper.generated" class KCMVisitor( private val codeGenerator: CodeGenerator, private val resolver: Resolver, - private val logger: KSPLogger + private val logger: KSPLogger, + private val configuration: KConMapperConfiguration, ) : KSVisitorVoid() { override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { @@ -63,7 +65,8 @@ class KCMVisitor( extensionFunctions += mappingFunctionGenerator.generateMappingFunction( targetClass = annotatedClass, sourceClass = sourceClass, - packageImports = packageImports + packageImports = packageImports, + configuration = configuration ) } } @@ -73,7 +76,8 @@ class KCMVisitor( extensionFunctions += mappingFunctionGenerator.generateMappingFunction( targetClass = targetClass, sourceClass = annotatedClass, - packageImports = packageImports + packageImports = packageImports, + configuration = configuration ) } } diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 0432bf5..3e760e4 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -4,8 +4,18 @@ plugins { idea } -sourceSets.configureEach { - kotlin.srcDir("$buildDir/generated/ksp/$name/kotlin/") +ksp { + // If set to true, this argument suppresses warnings about mapping mismatches, + // critical warnings are still emitted. + arg("kconmapper.suppressMappingMismatchWarnings", "false") +} + +kotlin { + jvmToolchain(17) + + sourceSets.main { + kotlin.srcDir("build/generated/ksp/main/kotlin") + } } dependencies { diff --git a/sample/src/main/kotlin/de/yanneckreiss/kconmapper/sample/generictypeexample/cat/CatCareStation.kt b/sample/src/main/kotlin/de/yanneckreiss/kconmapper/sample/generictypeexample/cat/CatCareStation.kt index de6ac02..6cd930e 100644 --- a/sample/src/main/kotlin/de/yanneckreiss/kconmapper/sample/generictypeexample/cat/CatCareStation.kt +++ b/sample/src/main/kotlin/de/yanneckreiss/kconmapper/sample/generictypeexample/cat/CatCareStation.kt @@ -5,7 +5,7 @@ import com.github.yanneckreiss.kconmapper.annotations.KConMapperProperty import de.yanneckreiss.kconmapper.sample.generictypeexample.AnimalCareStation import java.time.LocalDateTime -@KConMapper(fromClasses = [AnimalCareStation::class]) +@KConMapper(toClasses = [AnimalCareStation::class]) class CatCareStation( @KConMapperProperty(aliases = ["animalOne"])