diff --git a/jOOQ/pom.xml b/jOOQ/pom.xml index 3567eaded4..41ce6fae9c 100644 --- a/jOOQ/pom.xml +++ b/jOOQ/pom.xml @@ -101,6 +101,11 @@ provided true + + org.jetbrains.kotlin + kotlin-reflect + 1.2.30 + diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java index fac803a103..8dd34d9cbf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java @@ -50,6 +50,7 @@ import static org.jooq.impl.Tools.getMatchingMembers; import static org.jooq.impl.Tools.getMatchingSetters; import static org.jooq.impl.Tools.getPropertyName; import static org.jooq.impl.Tools.hasColumnAnnotations; +import static org.jooq.impl.Tools.fieldNameStrings; import static org.jooq.tools.reflect.Reflect.accessible; import java.beans.ConstructorProperties; @@ -79,6 +80,11 @@ import java.util.stream.Stream; import javax.persistence.Column; +import kotlin.jvm.JvmClassMappingKt; +import kotlin.reflect.KClass; +import kotlin.reflect.KFunction; +import kotlin.reflect.KParameter; +import kotlin.reflect.full.KClasses; import org.jooq.Attachable; import org.jooq.Configuration; import org.jooq.Field; @@ -343,6 +349,33 @@ public class DefaultRecordMapper implements RecordMapper kotlinClass = JvmClassMappingKt.getKotlinClass(type); + if (kotlinClass.isData()) { + KFunction primaryConstructor = KClasses.getPrimaryConstructor(kotlinClass); + List parameters = primaryConstructor.getParameters(); + + if (parameters.size() != fields.length) { + throw new MappingException("Primary constructor of Kotlin data class " + type + " did not match row type " + rowType); + } + + for (String fieldName : fieldNameStrings(fields)) { + String name = StringUtils.toCamelCaseLC(fieldName); + boolean found = false; + for (KParameter kParameter : parameters) { + String parameterName = kParameter.getName(); + if (name.equals(parameterName)) { + found = true; + } + } + if (!found) { + throw new MappingException("Primary constructor of Kotlin data class " + type + " did not match row type " + rowType); + } + } + delegate = new KotlinDataClassMapper(primaryConstructor); + return; + } + // [#1340] Allow for using non-public default constructors try { delegate = new MutablePOJOMapper(new ConstructorCall(accessible(type.getDeclaredConstructor())), instance); @@ -981,6 +1014,44 @@ public class DefaultRecordMapper implements RecordMapper { + + private final KFunction constructor; + private final List parameterNames; + private final Class[] parameterTypes; + private final Object[] parameterValues; + + KotlinDataClassMapper(KFunction constructor) { + this.constructor = constructor; + int n = constructor.getParameters().size(); + this.parameterNames = new ArrayList<>(n); + this.parameterValues = new Object[n]; + this.parameterTypes = new Class[n]; + + List parameters = constructor.getParameters(); + for (int i = 0; i < parameters.size(); i++) { + KParameter kParam = parameters.get(i); + parameterNames.add(kParam.getName()); + parameterTypes[i] = JvmClassMappingKt.getJavaClass(((KClass) kParam.getType().getClassifier())); + } + } + + @Override + public E map(R record) { + for (int i = 0; i < fields.length; i++) { + String name = StringUtils.toCamelCaseLC(fields[i].getName()); + int index = parameterNames.indexOf(name); + if (index < 0) { + throw new MappingException("An error ocurred when mapping record to " + type); + } + parameterValues[index] = record.get(i); + } + + Object[] converted = Convert.convert(parameterValues, parameterTypes); + return constructor.call(converted); + } + } + private static E attach(E attachable, Record record) { // [#2869] Attach the mapped outcome if it is Attachable and if the context's // Settings.attachRecords flag is set diff --git a/jOOQ/src/main/resources/META-INF/ABOUT.txt b/jOOQ/src/main/resources/META-INF/ABOUT.txt index d0eb994a84..a65a2c4a4c 100644 --- a/jOOQ/src/main/resources/META-INF/ABOUT.txt +++ b/jOOQ/src/main/resources/META-INF/ABOUT.txt @@ -55,6 +55,7 @@ Authors and contributors of jOOQ or parts of jOOQ in alphabetical order: - Victor Z. Peng - Vladimir Kulev - Vladimir Vinogradov +- Vojtech Polivka - Wang Gaoyuan - Zoltan Tamasi