From bf23fe11db5b814d20e59477990bbd48ee9244d1 Mon Sep 17 00:00:00 2001 From: Cyrill Halter <cyrill.halter@ergon.ch> Date: Wed, 24 Jul 2024 16:30:59 +0200 Subject: [PATCH] fix defaults and ensureTypes generation --- .../schwarz/crystalapi/util/CrystalWrap.kt | 68 ++++++++-- .../generation/model/CblDefaultGeneration.kt | 30 +++-- .../generation/model/EnsureTypesGeneration.kt | 44 +----- .../generation/model/EntityGeneration.kt | 2 +- .../model/RebindMethodGeneration.kt | 2 +- .../generation/model/WrapperGeneration.kt | 2 +- .../model/field/CblFieldHolder.kt | 127 ++++++++++-------- 7 files changed, 158 insertions(+), 117 deletions(-) diff --git a/crystal-map-api/src/main/java/com/schwarz/crystalapi/util/CrystalWrap.kt b/crystal-map-api/src/main/java/com/schwarz/crystalapi/util/CrystalWrap.kt index a33e3cd4..4494c189 100644 --- a/crystal-map-api/src/main/java/com/schwarz/crystalapi/util/CrystalWrap.kt +++ b/crystal-map-api/src/main/java/com/schwarz/crystalapi/util/CrystalWrap.kt @@ -39,26 +39,32 @@ object CrystalWrap { } } - inline fun <T> getList( + inline fun <reified T> getList( changes: MutableMap<String, Any?>, doc: MutableMap<String, out Any?>, fieldName: String, - mapper: ((List<MutableMap<String, Any?>>?) -> List<T>) + mapper: ((MutableMap<String, Any?>?) -> T?) ): List<T>? = (changes[fieldName] ?: doc[fieldName])?.let { value -> catchTypeConversionError(fieldName, value) { - mapper.invoke(value as List<MutableMap<String, Any?>>) + (value as List<Any>).mapNotNull { + catchTypeConversionError(fieldName, it) { + mapper.invoke(it as MutableMap<String, Any?>) + } + } } } - inline fun <T, reified U> getList( + inline fun <reified T, reified U> getList( changes: MutableMap<String, Any?>, doc: MutableMap<String, out Any?>, fieldName: String, typeConverter: ITypeConverter<T, U> ): List<T>? = (changes[fieldName] ?: doc[fieldName])?.let { value -> catchTypeConversionError(fieldName, value) { - ((value as List<Any>).map { it as U }).mapNotNull { - typeConverter.read(it) + (value as List<Any>).mapNotNull { + catchTypeConversionError(fieldName, it) { + typeConverter.read(it as U) + } } } } @@ -69,7 +75,11 @@ object CrystalWrap { fieldName: String ): List<T>? = (changes[fieldName] ?: doc[fieldName])?.let { value -> catchTypeConversionError(fieldName, value) { - (value as List<Any>).map { it as T } + (value as List<Any>).mapNotNull { + catchTypeConversionError(fieldName, it) { + it as T + } + } } } @@ -134,12 +144,50 @@ object CrystalWrap { } } - inline fun <reified T> catchTypeConversionError(fieldName: String, value: Any, task: () -> T): T? = try { + inline fun <reified DomainType> ensureType( + map: HashMap<String, in Any>, + key: String, + typeConverter: ITypeConverter<DomainType, *> + ) { + val value = map[key] + catchTypeConversionError(key, value) { + if (value != null && value is DomainType) { + val converted = typeConverter.write(value) + converted?.let { map.replace(key, it) } + } + } + } + + inline fun <reified DomainType, reified MapType> ensureListType( + map: HashMap<String, in Any>, + key: String, + typeConverter: ITypeConverter<DomainType, MapType> + ) { + val value = map[key] + if (value != null && value is List<*>) { + val converted = value.map { + if (it != null && it is DomainType) { + catchTypeConversionError<MapType?>(key, it) { + typeConverter.write(it) + } + } else { + it + } + } + map.replace(key, converted) + } + } + + inline fun <reified T> catchTypeConversionError( + fieldName: String, + value: Any?, + task: () -> T + ): T? = try { task() - } catch (cce: ClassCastException) { + } catch (e: Exception) { PersistenceConfig.onTypeConversionError( com.schwarz.crystalapi.TypeConversionErrorWrapper( - cce, + e, fieldName, value, T::class diff --git a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/CblDefaultGeneration.kt b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/CblDefaultGeneration.kt index de4fd648..9146fb63 100644 --- a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/CblDefaultGeneration.kt +++ b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/CblDefaultGeneration.kt @@ -1,15 +1,17 @@ package com.schwarz.crystalprocessor.generation.model import com.schwarz.crystalprocessor.model.entity.BaseEntityHolder +import com.schwarz.crystalprocessor.model.typeconverter.TypeConverterHolderForEntityGeneration import com.schwarz.crystalprocessor.util.ConversionUtil import com.schwarz.crystalprocessor.util.TypeUtil import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.TypeName object CblDefaultGeneration { - fun addDefaults(holder: BaseEntityHolder, useNullableMap: Boolean): FunSpec { + fun addDefaults(holder: BaseEntityHolder, useNullableMap: Boolean, typeConvertersByConvertedClass: Map<TypeName, TypeConverterHolderForEntityGeneration>): FunSpec { val type = if (useNullableMap) TypeUtil.mutableMapStringAnyNullable() else TypeUtil.mutableMapStringAny() val valueType = @@ -19,23 +21,35 @@ object CblDefaultGeneration { if (useNullableMap) TypeUtil.anyNullable() else TypeUtil.any() val builder = - FunSpec.builder("addDefaults").addModifiers(KModifier.PRIVATE) + FunSpec.builder("addDefaults").addModifiers(KModifier.PRIVATE).addParameter("map", type) + + builder.addStatement("val result = mutableMapOf<String, Any?>()") for (fieldHolder in holder.fields.values) { if (fieldHolder.isDefault) { - builder.addStatement( - "this.%N = ${ConversionUtil.convertStringToDesiredFormat( + fieldHolder.crystalWrapSetStatement( + builder, + "result", + typeConvertersByConvertedClass, + ConversionUtil.convertStringToDesiredFormat( fieldHolder.typeMirror, fieldHolder.defaultValue - )}", - fieldHolder.accessorSuffix() + ) ) } } + + builder.addCode( + CodeBlock.builder() + .beginControlFlow("result.forEach") + .beginControlFlow("if(it.value != null)").addStatement("map[it.key] = it.value!!").endControlFlow() + .endControlFlow() + .build() + ) return builder.build() } - fun addAddCall(): CodeBlock { - return CodeBlock.builder().addStatement("addDefaults()").build() + fun addAddCall(nameOfMap: String): CodeBlock { + return CodeBlock.builder().addStatement("addDefaults(%N)", nameOfMap).build() } } diff --git a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EnsureTypesGeneration.kt b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EnsureTypesGeneration.kt index da3f2028..68cca4ff 100644 --- a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EnsureTypesGeneration.kt +++ b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EnsureTypesGeneration.kt @@ -19,55 +19,17 @@ object EnsureTypesGeneration { val explicitType = if (useNullableMap) TypeUtil.hashMapStringAnyNullable() else TypeUtil.hashMapStringAny() val type = if (useNullableMap) TypeUtil.mapStringAnyNullable() else TypeUtil.mapStringAny() - val typeConversionReturnType = - if (useNullableMap) TypeUtil.anyNullable() else TypeUtil.any() val ensureTypes = FunSpec.builder("ensureTypes").addParameter("doc", type).returns(type) ensureTypes.addStatement("val %N = %T()", RESULT_VAL_NAME, explicitType) ensureTypes.addStatement("%N.putAll(doc)", RESULT_VAL_NAME) for (field in holder.fields.values) { - if (field.isNonConvertibleClass) { - if (field.isIterable) { - ensureTypes.addStatement( - "%T.getList<%T>(mutableMapOf(), %N, %N)", - CrystalWrap::class, - field.fieldType, - RESULT_VAL_NAME, - field.constantName - ) - } else { - ensureTypes.addStatement( - "%T.get<%T>(mutableMapOf(), %N, %N)", - CrystalWrap::class, - field.fieldType, - RESULT_VAL_NAME, - field.constantName - ) - } - } else if (field.isTypeOfSubEntity) { - if (field.isIterable) { - ensureTypes.addStatement( - "%T.getList(mutableMapOf(), %N, %N, {%T.fromMap(it) ?: emptyList()})", - CrystalWrap::class, - RESULT_VAL_NAME, - field.constantName, - field.subEntityTypeName - ) - } else { - ensureTypes.addStatement( - "%T.get(mutableMapOf(), %N, %N, {%T.fromMap(it)})", - CrystalWrap::class, - RESULT_VAL_NAME, - field.constantName, - field.subEntityTypeName - ) - } - } else { + if (!field.isNonConvertibleClass && !field.isTypeOfSubEntity) { val typeConverterHolder = typeConvertersByConvertedClass.get(field.fieldType)!! if (field.isIterable) { ensureTypes.addStatement( - "%T.getList(mutableMapOf(), %N, %N, %T)", + "%T.ensureListType(%N, %N, %T)", CrystalWrap::class, RESULT_VAL_NAME, field.constantName, @@ -75,7 +37,7 @@ object EnsureTypesGeneration { ) } else { ensureTypes.addStatement( - "%T.get(mutableMapOf(), %N, %N, %T)", + "%T.ensureType(%N, %N, %T)", CrystalWrap::class, RESULT_VAL_NAME, field.constantName, diff --git a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EntityGeneration.kt b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EntityGeneration.kt index 551037bf..8c7286fc 100644 --- a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EntityGeneration.kt +++ b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/EntityGeneration.kt @@ -69,7 +69,7 @@ class EntityGeneration { .addSuperinterface(MandatoryCheck::class) .addProperty(holder.dbNameProperty()) .addFunction(EnsureTypesGeneration.ensureTypes(holder, false, typeConvertersByConvertedClass)) - .addFunction(CblDefaultGeneration.addDefaults(holder, false)) + .addFunction(CblDefaultGeneration.addDefaults(holder, false, typeConvertersByConvertedClass)) .addFunction(CblConstantGeneration.addConstants(holder, false)) .addFunction(ValidateMethodGeneration.generate(holder, true)) .addProperty( diff --git a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/RebindMethodGeneration.kt b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/RebindMethodGeneration.kt index 120e6fbe..efe03046 100644 --- a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/RebindMethodGeneration.kt +++ b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/RebindMethodGeneration.kt @@ -11,7 +11,7 @@ class RebindMethodGeneration { val type = if (clearMDocChanges) TypeUtil.mapStringAny() else TypeUtil.mapStringAnyNullable() val rebind = FunSpec.builder("rebind").addParameter("doc", type) .addStatement("mDoc = %T()", explicitType) - .addCode(CblDefaultGeneration.addAddCall()) + .addCode(CblDefaultGeneration.addAddCall("mDoc")) .addCode( CodeBlock.builder() .beginControlFlow("if(doc != null)") diff --git a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/WrapperGeneration.kt b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/WrapperGeneration.kt index 07a05274..f45aae7f 100644 --- a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/WrapperGeneration.kt +++ b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/generation/model/WrapperGeneration.kt @@ -23,7 +23,7 @@ class WrapperGeneration { .addSuperinterface(holder.interfaceTypeName) .addSuperinterface(MandatoryCheck::class) .addFunction(EnsureTypesGeneration.ensureTypes(holder, true, typeConvertersByConvertedClass)) - .addFunction(CblDefaultGeneration.addDefaults(holder, true)) + .addFunction(CblDefaultGeneration.addDefaults(holder, true, typeConvertersByConvertedClass)) .addFunction(CblConstantGeneration.addConstants(holder, true)) .addFunction(SetAllMethodGeneration().generate(holder, false)) .addFunction(MapSupportGeneration.toMap(holder)) diff --git a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/model/field/CblFieldHolder.kt b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/model/field/CblFieldHolder.kt index f569a280..946bee2c 100644 --- a/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/model/field/CblFieldHolder.kt +++ b/crystal-map-processor/src/main/java/com/schwarz/crystalprocessor/model/field/CblFieldHolder.kt @@ -101,12 +101,6 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix fieldType, constantName ) - setter.addStatement( - "%T.setList(%N, %N, value)", - CrystalWrap::class, - if (useMDocChanges) "mDocChanges" else "mDoc", - constantName - ) } else { getter.addStatement( "return %T.get<%T>($mDocPhrase, %N)".forceCastIfMandatory( @@ -116,17 +110,11 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix fieldType, constantName ) - setter.addStatement( - "%T.set(%N, %N, value)", - CrystalWrap::class, - if (useMDocChanges) "mDocChanges" else "mDoc", - constantName - ) } } else if (isTypeOfSubEntity) { if (isIterable) { getter.addStatement( - "return %T.getList<%T>($mDocPhrase, %N, {%T.fromMap(it) ?: emptyList()})".forceCastIfMandatory( + "return %T.getList<%T>($mDocPhrase, %N, {%T.fromMap(it)})".forceCastIfMandatory( mandatory ), CrystalWrap::class, @@ -134,13 +122,6 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix constantName, subEntityTypeName ) - setter.addStatement( - "%T.setList(%N, %N, value, {%T.toMap(it)})", - CrystalWrap::class, - if (useMDocChanges) "mDocChanges" else "mDoc", - constantName, - subEntityTypeName - ) } else { getter.addStatement( "return %T.get<%T>($mDocPhrase, %N, {%T.fromMap(it)})".forceCastIfMandatory( @@ -151,13 +132,6 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix constantName, subEntityTypeName ) - setter.addStatement( - "%T.set(%N, %N, value, {%T.toMap(it)})", - CrystalWrap::class, - if (useMDocChanges) "mDocChanges" else "mDoc", - constantName, - subEntityTypeName - ) } } else { val typeConverterHolder = @@ -171,14 +145,6 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix constantName, typeConverterHolder.instanceClassTypeName ) - - setter.addStatement( - "%T.setList(%N, %N, value, %T)", - CrystalWrap::class, - if (useMDocChanges) "mDocChanges" else "mDoc", - constantName, - typeConverterHolder.instanceClassTypeName - ) } else { getter.addStatement( "return %T.get($mDocPhrase, %N, %T)".forceCastIfMandatory( @@ -188,17 +154,11 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix constantName, typeConverterHolder.instanceClassTypeName ) - - setter.addStatement( - "%T.set(%N, %N, value, %T)", - CrystalWrap::class, - if (useMDocChanges) "mDocChanges" else "mDoc", - constantName, - typeConverterHolder.instanceClassTypeName - ) } } + crystalWrapSetStatement(setter, if (useMDocChanges) "mDocChanges" else "mDoc", typeConvertersByConvertedClass, "value") + if (comment.isNotEmpty()) { propertyBuilder.addKdoc(KDocGeneration.generate(comment)) } @@ -206,6 +166,75 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix return propertyBuilder.setter(setter.build()).getter(getter.build()).build() } + fun crystalWrapSetStatement( + setter: FunSpec.Builder, + mDocPhrase: String, + typeConvertersByConvertedClass: Map<TypeName, TypeConverterHolderForEntityGeneration>, + valueName: String + ) { + if (isNonConvertibleClass) { + if (isIterable) { + setter.addStatement( + "%T.setList(%N, %N, %L)", + CrystalWrap::class, + mDocPhrase, + constantName, + valueName + ) + } else { + setter.addStatement( + "%T.set(%N, %N, %L)", + CrystalWrap::class, + mDocPhrase, + constantName, + valueName + ) + } + } else if (isTypeOfSubEntity) { + if (isIterable) { + setter.addStatement( + "%T.setList(%N, %N, %L, {%T.toMap(it)})", + CrystalWrap::class, + mDocPhrase, + constantName, + valueName, + subEntityTypeName + ) + } else { + setter.addStatement( + "%T.set(%N, %N, %L, {%T.toMap(it)})", + CrystalWrap::class, + mDocPhrase, + constantName, + valueName, + subEntityTypeName + ) + } + } else { + val typeConverterHolder = + typeConvertersByConvertedClass.get(fieldType)!! + if (isIterable) { + setter.addStatement( + "%T.setList(%N, %N, %L, %T)", + CrystalWrap::class, + mDocPhrase, + constantName, + valueName, + typeConverterHolder.instanceClassTypeName + ) + } else { + setter.addStatement( + "%T.set(%N, %N, %L, %T)", + CrystalWrap::class, + mDocPhrase, + constantName, + valueName, + typeConverterHolder.instanceClassTypeName + ) + } + } + } + override fun builderSetter( dbName: String?, packageName: String, @@ -244,18 +273,6 @@ class CblFieldHolder(field: Field, classPaths: List<String>, subEntityNameSuffix return listOf(fieldAccessorConstant) } - fun evaluateClazzForTypeConversion(): TypeName { - return if (isIterable) { - if (TypeUtil.isMap(fieldType)) { - TypeUtil.string() - } else { - fieldType - } - } else { - TypeUtil.parseMetaType(typeMirror, isIterable, false, subEntitySimpleName) - } - } - private fun String.forceCastIfMandatory(mandatory: Boolean): String { if (mandatory) { return "$this!!"