Skip to content

Commit

Permalink
Introduced configuration parameters
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
YanneckReiss committed Jun 19, 2023
1 parent a34de4a commit 83a8240
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 36 deletions.
21 changes: 17 additions & 4 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
}
```
</details>
Expand All @@ -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")
}
```
</details>
Expand Down
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -39,8 +39,8 @@ subprojects {
}

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

withSourcesJar()
withJavadocJar()
Expand All @@ -59,7 +59,7 @@ subprojects {
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
jvmTarget = "17"
}
}
}
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion jitpack.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
jdk:
- openjdk11
- openjdk17
Original file line number Diff line number Diff line change
Expand Up @@ -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<KClass<*>> = [],
val fromClasses: Array<KClass<*>> = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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<KSAnnotated> {
val annotationPackagePath = "$KCONMAPPER_PACKAGE_NAME.$KCONMAPPER_ANNOTATIONS_PACKAGE_NAME.$KCONMAPPER_ANNOTATION_NAME"
val resolvedSymbols: Sequence<KSAnnotated> = resolver.getSymbolsWithAnnotation(annotationName = annotationPackagePath)
val annotationPackagePath =
"$KCONMAPPER_PACKAGE_NAME.$KCONMAPPER_ANNOTATIONS_PACKAGE_NAME.$KCONMAPPER_ANNOTATION_NAME"
val resolvedSymbols: Sequence<KSAnnotated> =
resolver.getSymbolsWithAnnotation(annotationName = annotationPackagePath)

resolvedSymbols
.filter { ksAnnotated -> ksAnnotated is KSClassDeclaration && ksAnnotated.validate() }
Expand All @@ -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
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String>) : this(
suppressMappingMismatchWarnings = options[OPTION_SUPPRESS_MAPPING_MISMATCH_WARNINGS]?.toBoolean() ?: false
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -56,7 +58,8 @@ class MappingFunctionGenerator(
targetClass = targetClass,
targetClassTypeParameters = targetClassTypeParameters,
targetClassName = targetClassName,
packageImports = packageImports
packageImports = packageImports,
configuration = configuration
)
}

Expand All @@ -65,7 +68,8 @@ class MappingFunctionGenerator(
targetClass: KSClassDeclaration,
targetClassTypeParameters: List<KSTypeParameter>,
targetClassName: String,
packageImports: PackageImports
packageImports: PackageImports,
configuration: KConMapperConfiguration
): String {

var extensionFunctions = ""
Expand All @@ -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` " +
Expand All @@ -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
Expand All @@ -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 ->
Expand Down Expand Up @@ -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
Expand All @@ -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"
}

Expand Down Expand Up @@ -255,7 +277,8 @@ class MappingFunctionGenerator(
targetClassTypeParameters: List<KSTypeParameter>,
packageImports: PackageImports,
sourceClassName: String,
targetClassName: String
targetClassName: String,
configuration: KConMapperConfiguration
): Pair<MutableList<KSValueParameter>, MutableList<MatchingArgument>> {

val missingArguments = mutableListOf<KSValueParameter>()
Expand All @@ -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<String>?
val aliases: Set<String> = findKConMapperPropertyAliases(parameterFromSourceClass.annotations + valueParam.annotations)
val aliases: Set<String> =
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 }) {
Expand Down Expand Up @@ -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}` " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -63,7 +65,8 @@ class KCMVisitor(
extensionFunctions += mappingFunctionGenerator.generateMappingFunction(
targetClass = annotatedClass,
sourceClass = sourceClass,
packageImports = packageImports
packageImports = packageImports,
configuration = configuration
)
}
}
Expand All @@ -73,7 +76,8 @@ class KCMVisitor(
extensionFunctions += mappingFunctionGenerator.generateMappingFunction(
targetClass = targetClass,
sourceClass = annotatedClass,
packageImports = packageImports
packageImports = packageImports,
configuration = configuration
)
}
}
Expand Down
14 changes: 12 additions & 2 deletions sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down

0 comments on commit 83a8240

Please sign in to comment.