From b807eb3b4674e5a5776de443391ec57263f8cda4 Mon Sep 17 00:00:00 2001 From: Vojtech Polivka Date: Mon, 19 Mar 2018 13:51:57 -0400 Subject: [PATCH 1/4] [#7324] Support Kotlin data classes in DefaultRecordMapper --- jOOQ/pom.xml | 5 ++ .../org/jooq/impl/DefaultRecordMapper.java | 71 +++++++++++++++++++ jOOQ/src/main/resources/META-INF/ABOUT.txt | 1 + 3 files changed, 77 insertions(+) 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 From 0b6559e258bebe648e44d76c977f72e62536e00f Mon Sep 17 00:00:00 2001 From: Vojtech Polivka Date: Tue, 20 Mar 2018 11:15:54 -0400 Subject: [PATCH 2/4] Revert "[#7324] Support Kotlin data classes in DefaultRecordMapper" --- jOOQ/pom.xml | 5 -- .../org/jooq/impl/DefaultRecordMapper.java | 71 ------------------- jOOQ/src/main/resources/META-INF/ABOUT.txt | 1 - 3 files changed, 77 deletions(-) diff --git a/jOOQ/pom.xml b/jOOQ/pom.xml index 41ce6fae9c..3567eaded4 100644 --- a/jOOQ/pom.xml +++ b/jOOQ/pom.xml @@ -101,11 +101,6 @@ 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 8dd34d9cbf..fac803a103 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java @@ -50,7 +50,6 @@ 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; @@ -80,11 +79,6 @@ 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; @@ -349,33 +343,6 @@ 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); @@ -1014,44 +981,6 @@ 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 a65a2c4a4c..d0eb994a84 100644 --- a/jOOQ/src/main/resources/META-INF/ABOUT.txt +++ b/jOOQ/src/main/resources/META-INF/ABOUT.txt @@ -55,7 +55,6 @@ 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 From 9ef11f1791d7935e621cac62b450255717c8a5aa Mon Sep 17 00:00:00 2001 From: Vojtech Polivka Date: Tue, 20 Mar 2018 13:15:44 -0400 Subject: [PATCH 3/4] [#7324] Use meta-reflection to get rid of dependencies. Reuse ImmutablePOJOMapperWithParameterNames. --- .../org/jooq/impl/DefaultRecordMapper.java | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java index fac803a103..508c1b72ee 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java @@ -54,15 +54,8 @@ import static org.jooq.tools.reflect.Reflect.accessible; import java.beans.ConstructorProperties; import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.*; import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Proxy; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; @@ -92,6 +85,7 @@ import org.jooq.exception.MappingException; import org.jooq.tools.Convert; import org.jooq.tools.StringUtils; import org.jooq.tools.reflect.Reflect; +import org.jooq.tools.reflect.ReflectException; /** * This is the default implementation for RecordMapper types. @@ -375,6 +369,38 @@ public class DefaultRecordMapper implements RecordMapper klassType = Reflect.on("kotlin.reflect.KClass").type(); + Method getJavaClass = JvmClassMappingKt.type().getMethod("getJavaClass", klassType); + + List parameterNames = new ArrayList<>(); + Class[] parameterTypes = new Class[parameters.size()]; + for (int i = 0; i < parameters.size(); i++) { + Reflect parameter = Reflect.on(parameters.get(i)); + parameterNames.add(parameter.call("getName").get()); + Object typeClassifier = parameter.call("getType").call("getClassifier").get(); + parameterTypes[i] = (Class) getJavaClass.invoke(JvmClassMappingKt.get(), typeClassifier); + } + + Constructor javaConstructor = (Constructor) this.type.getConstructor(parameterTypes); + delegate = new ImmutablePOJOMapperWithParameterNames(javaConstructor, parameterNames); + return; + } + } catch (ReflectException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // do nothing + } + + boolean mapConstructorParameterNames = TRUE.equals(configuration.settings().isMapConstructorParameterNames()); From 46fb601a438504cb07e2f56102355c300865aea6 Mon Sep 17 00:00:00 2001 From: Vojtech Polivka Date: Mon, 19 Mar 2018 13:51:57 -0400 Subject: [PATCH 4/4] [#7324] Add contributor --- jOOQ/src/main/resources/META-INF/ABOUT.txt | 1 + 1 file changed, 1 insertion(+) 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