Skip to content

Commit

Permalink
Merge pull request #64 from SchwarzIT/feature/NFWWS-7688-recognise-da…
Browse files Browse the repository at this point in the history
…tatype-issues-in-entities

 NFWWS-7688: Redirect data class type changes
  • Loading branch information
mpradzin authored Oct 28, 2022
2 parents 1de70c3 + ef69250 commit 57da485
Show file tree
Hide file tree
Showing 33 changed files with 546 additions and 213 deletions.
9 changes: 6 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.7.0'
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
Expand All @@ -19,16 +19,19 @@ buildscript {
}
}


subprojects {
apply plugin: "org.jlleitschuh.gradle.ktlint"

repositories {
mavenCentral()
}

// Optionally configure plugin
ktlint {

filter {
exclude { it.file.path.contains("$buildDir/generated/") }
}

disabledRules = [
"import-ordering",
"chain-wrapping",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object PersistenceConfig {
private var mConnector: Connector? = null
private var mSuspendingConnector: SuspendingConnector? = null

interface Connector {
interface Connector : TypeConversionErrorCallback {
val typeConversions: Map<KClass<*>, TypeConversion>
fun getDocument(id: String, dbName: String, onlyInclude: List<String>? = null): Map<String, Any>?
fun getDocuments(ids: List<String>, dbName: String, onlyInclude: List<String>? = null): List<Map<String, Any>?>
Expand All @@ -19,7 +19,7 @@ object PersistenceConfig {
fun upsertDocument(document: MutableMap<String, Any>, id: String?, dbName: String): Map<String, Any>
}

interface SuspendingConnector {
interface SuspendingConnector : TypeConversionErrorCallback {

val typeConversions: Map<KClass<*>, TypeConversion>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package kaufland.com.coachbasebinderapi;

import org.jetbrains.annotations.Nullable;

public interface TypeConversion {

Object write(Object value);
@Nullable
Object write(@Nullable Object value);

Object read(Object value);
@Nullable
Object read(@Nullable Object value);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package kaufland.com.coachbasebinderapi

interface TypeConversionErrorCallback {
fun invokeOnError(errorWrapper: TypeConversionErrorWrapper)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package kaufland.com.coachbasebinderapi

import kotlin.reflect.KClass

data class TypeConversionErrorWrapper(
val exception: Exception,
val fieldName: String,
val value: Any?,
val `class`: KClass<*>
)
5 changes: 2 additions & 3 deletions couchbase-entity-connector/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 29
compileSdkVersion 33

defaultConfig {
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 33
versionCode 1
versionName "1.0"

Expand All @@ -25,7 +25,6 @@ android {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
buildToolsVersion = '29.0.2'
}

group = 'com.github.SchwarzIT'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ abstract class Couchbase2Connector : PersistenceConfig.Connector {
init {
mTypeConversions[Int::class] = object : TypeConversion {

override fun write(value: Any): Any {
override fun write(value: Any?): Any? {
return value
}

override fun read(value: Any): Any {
override fun read(value: Any?): Any? {
if (value is Number) {
return value.toInt()
}
if (value is Iterable<*>) {
val result = ArrayList<Any>()
for (itValue in value) {
itValue?.let {
result.add(read(itValue))
read(itValue)?.let { it1 -> result.add(it1) }
}
}
return result
Expand All @@ -38,19 +38,19 @@ abstract class Couchbase2Connector : PersistenceConfig.Connector {
}
}
mTypeConversions[Double::class] = object : TypeConversion {
override fun write(value: Any): Any {
override fun write(value: Any?): Any? {
return value
}

override fun read(value: Any): Any {
override fun read(value: Any?): Any? {
if (value is Number) {
return value.toDouble()
}
if (value is Iterable<*>) {
val result = ArrayList<Any>()
for (itValue in value) {
itValue?.let {
result.add(read(itValue))
read(itValue)?.let { it1 -> result.add(it1) }
}
}
return result
Expand Down Expand Up @@ -150,16 +150,16 @@ abstract class Couchbase2Connector : PersistenceConfig.Connector {
}

@Throws(PersistenceException::class)
override fun upsertDocument(upsert: MutableMap<String, Any>, docId: String?, name: String): Map<String, Any> {
if (upsert["_id"] == null && docId != null) {
upsert["_id"] = docId
override fun upsertDocument(document: MutableMap<String, Any>, id: String?, dbName: String): Map<String, Any> {
if (document["_id"] == null && id != null) {
document["_id"] = id
}
val unsavedDoc = MutableDocument(docId, upsert)
val unsavedDoc = MutableDocument(id, document)
return try {
upsert["_id"] = unsavedDoc.id
document["_id"] = unsavedDoc.id
unsavedDoc.setString("_id", unsavedDoc.id)
getDatabase(name).save(unsavedDoc)
upsert
getDatabase(dbName).save(unsavedDoc)
document
} catch (e: CouchbaseLiteException) {
throw PersistenceException(e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,29 @@ object CblDefaultGeneration {

fun addDefaults(holder: BaseEntityHolder, useNullableMap: Boolean): FunSpec {

val type = if (useNullableMap) TypeUtil.mutableMapStringAnyNullable() else TypeUtil.mutableMapStringAny()
val typeConversionReturnType = if (useNullableMap) TypeUtil.anyNullable() else TypeUtil.any()
val type =
if (useNullableMap) TypeUtil.mutableMapStringAnyNullable() else TypeUtil.mutableMapStringAny()
val typeConversionReturnType =
if (useNullableMap) TypeUtil.anyNullable() else TypeUtil.any()

val builder = FunSpec.builder("addDefaults").addModifiers(KModifier.PRIVATE).addParameter("map", type)
val builder =
FunSpec.builder("addDefaults").addModifiers(KModifier.PRIVATE).addParameter("map", type)

for (fieldHolder in holder.fields.values) {

if (fieldHolder.isDefault) {
builder.beginControlFlow("if(map[%N] == null)", fieldHolder.constantName)
builder.addStatement("map.put(%N, " + fieldHolder.ensureType(typeConversionReturnType, ConversionUtil.convertStringToDesiredFormat(fieldHolder.typeMirror, fieldHolder.defaultValue)) + "!!)", fieldHolder.constantName)
builder.addStatement(
"map.put(%N, " + fieldHolder.ensureType(
typeConversionReturnType,
ConversionUtil.convertStringToDesiredFormat(
fieldHolder.typeMirror,
fieldHolder.defaultValue
) + ", %N",
fieldHolder.constantName
) + "!!)",
fieldHolder.constantName
)
builder.endControlFlow()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ class CommonInterfaceGeneration {

fun generateModel(holder: BaseEntityHolder): FileSpec {

var interfaceSpec = TypeSpec.interfaceBuilder(holder.interfaceSimpleName)
val interfaceSpec = TypeSpec.interfaceBuilder(holder.interfaceSimpleName)
interfaceSpec.addSuperinterface(TypeUtil.mapSupport())
holder.basedOn.forEach { interfaceSpec.addSuperinterface(it.interfaceTypeName) }

var companionSpec = TypeSpec.companionObjectBuilder()
val companionSpec = TypeSpec.companionObjectBuilder()

for (fieldHolder in holder.allFields) {
val isBaseField = holder.basedOn.any {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,26 @@ object EnsureTypesGeneration {

fun ensureTypes(holder: BaseEntityHolder, useNullableMap: Boolean): FunSpec {

val explicitType = if (useNullableMap) TypeUtil.hashMapStringAnyNullable() else TypeUtil.hashMapStringAny()
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 typeConversionReturnType =
if (useNullableMap) TypeUtil.anyNullable() else TypeUtil.any()
val ensureTypes = FunSpec.builder("ensureTypes").addParameter("doc", type).returns(type)
ensureTypes.addStatement("val result = %T()", explicitType)
ensureTypes.addStatement("result.putAll(doc)")

for (field in holder.fields.values) {
ensureTypes.beginControlFlow("${field.ensureType(typeConversionReturnType,"doc[%N]", field.constantName)}?.let")
ensureTypes.beginControlFlow(
"${
field.ensureType(
typeConversionReturnType,
"doc[%N], %N",
field.constantName,
field.constantName
)
}?.let"
)
ensureTypes.addStatement("result[%N] = it", field.constantName)
ensureTypes.endControlFlow()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import com.kaufland.model.entity.EntityHolder
import com.kaufland.model.id.DocIdHolder
import com.kaufland.util.TypeUtil
import com.kaufland.util.TypeUtil.string
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.jvm.throws
import kaufland.com.coachbasebinderapi.Entity
import kaufland.com.coachbasebinderapi.PersistenceConfig
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,65 @@ package com.kaufland.generation.model
import com.kaufland.util.TypeUtil
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.TypeVariableName
import kaufland.com.coachbasebinderapi.PersistenceConfig
import kaufland.com.coachbasebinderapi.TypeConversionErrorWrapper

class TypeConversionMethodsGeneration(val useSuspend: Boolean) {
class TypeConversionMethodsGeneration(private val useSuspend: Boolean) {

fun generate(): Collection<FunSpec> {
return listOf(
FunSpec.builder(READ_METHOD_NAME)
.addAnnotation(JvmStatic::class)
FunSpec.builder(READ_METHOD_NAME).addAnnotation(JvmStatic::class)
.addParameter("value", TypeUtil.any().copy(nullable = true))
.addParameter("fieldName", TypeUtil.string())
.addParameter("clazz", TypeUtil.classStar())
.addTypeVariable(TypeVariableName.invoke("T"))
.returns(TypeVariableName.invoke("T?"))
.addCode(CodeBlock.builder().addStatement("val conversion = %T.${getTypeConversionMethod(useSuspend)}.get(clazz)", PersistenceConfig::class).beginControlFlow("if(conversion == null)").addStatement("return value as T?").endControlFlow().addStatement("return conversion?.read(value) as T?").build())
.build(),
.addTypeVariable(TypeVariableName.invoke("reified T"))
/** has to be inlined because the type inference would otherwise happen in the properties and cause there an classcastexception */
.addModifiers(KModifier.INLINE).returns(TypeVariableName.invoke("T?"))
/** use empty space otherwise its recognized as a single-expression*/
.addCode(CodeBlock.of(" return ")).addCode(
CodeBlock.builder().beginControlFlow("try").addStatement(
"val conversion = %T.${getTypeConversionMethod(useSuspend)}[clazz] ?:\n return value as T?",
PersistenceConfig::class
).addStatement("return conversion.read(value) as T?").endControlFlow()
.beginControlFlow("catch(ex: %T)", java.lang.Exception::class).addStatement(
"%T.${getConnector(useSuspend)}.invokeOnError(${TypeConversionErrorWrapper::class.qualifiedName}(ex, fieldName, value, clazz))",
PersistenceConfig::class
).addStatement("null").endControlFlow().build()
).build(),

FunSpec.builder(WRITE_METHOD_NAME)
.addAnnotation(JvmStatic::class)
FunSpec.builder(WRITE_METHOD_NAME).addAnnotation(JvmStatic::class)
.addParameter("value", TypeUtil.any().copy(nullable = true))
.addParameter("fieldName", TypeUtil.string())
.addParameter("clazz", TypeUtil.classStar())
.addTypeVariable(TypeVariableName.invoke("T"))
.returns(TypeVariableName.invoke("T?"))
.addCode(CodeBlock.builder().addStatement("val conversion = %T.${getTypeConversionMethod(useSuspend)}.get(clazz)", PersistenceConfig::class).beginControlFlow("if(conversion == null)").addStatement("return value as T?").endControlFlow().addStatement("return conversion.write(value) as T?").build())
.build()

.addTypeVariable(TypeVariableName.invoke("reified T"))
/** has to be inlined because the type inference would otherwise happen in the properties and cause there an classcastexception */
.addModifiers(KModifier.INLINE).returns(TypeVariableName.invoke("T?"))
/** use empty space otherwise its recognized as a single-expression*/
.addCode(CodeBlock.of(" return ")).addCode(
CodeBlock.builder().beginControlFlow("try").addStatement(
"val conversion = %T.${getTypeConversionMethod(useSuspend)}[clazz] ?:\n return value as T?",
PersistenceConfig::class
).addStatement("return conversion.write(value) as T?").endControlFlow()
.beginControlFlow("catch(ex: %T)", java.lang.Exception::class).addStatement(
"%T.${getConnector(useSuspend)}.invokeOnError(${TypeConversionErrorWrapper::class.qualifiedName}(ex, fieldName, value, clazz))",
PersistenceConfig::class
).addStatement("null").endControlFlow().build()
).build()
)
}

companion object {

val READ_METHOD_NAME = "read"

val WRITE_METHOD_NAME = "write"
const val READ_METHOD_NAME = "read"
const val WRITE_METHOD_NAME = "write"

private fun getTypeConversionMethod(useSuspend: Boolean): String {
return "${if (useSuspend) "suspendingConnector" else "connector"}.typeConversions"
return "${getConnector(useSuspend)}.typeConversions"
}

private fun getConnector(useSuspend: Boolean): String {
return if (useSuspend) "suspendingConnector" else "connector"
}
}
}
Loading

0 comments on commit 57da485

Please sign in to comment.