@@ -19,16 +19,20 @@ import org.jetbrains.kotlinx.dataframe.impl.columnName
19
19
import org.jetbrains.kotlinx.dataframe.impl.emptyPath
20
20
import org.jetbrains.kotlinx.dataframe.impl.getListType
21
21
import org.jetbrains.kotlinx.dataframe.impl.projectUpTo
22
- import org.jetbrains.kotlinx.dataframe.impl.schema.getPropertiesOrder
22
+ import org.jetbrains.kotlinx.dataframe.impl.schema.getPropertyOrderFromAnyConstructor
23
+ import org.jetbrains.kotlinx.dataframe.impl.schema.getPropertyOrderFromPrimaryConstructor
23
24
import java.lang.reflect.InvocationTargetException
24
25
import java.lang.reflect.Method
25
26
import java.time.temporal.Temporal
27
+ import kotlin.reflect.KCallable
26
28
import kotlin.reflect.KClass
27
29
import kotlin.reflect.KProperty
28
30
import kotlin.reflect.KVisibility
29
31
import kotlin.reflect.full.isSubclassOf
32
+ import kotlin.reflect.full.memberFunctions
30
33
import kotlin.reflect.full.memberProperties
31
34
import kotlin.reflect.full.primaryConstructor
35
+ import kotlin.reflect.full.valueParameters
32
36
import kotlin.reflect.full.withNullability
33
37
import kotlin.reflect.jvm.isAccessible
34
38
import kotlin.reflect.jvm.javaField
@@ -72,13 +76,13 @@ internal class CreateDataFrameDslImpl<T>(
72
76
73
77
internal class TraverseConfiguration : TraversePropertiesDsl {
74
78
75
- val excludeProperties = mutableSetOf<KProperty <* >>()
79
+ val excludeProperties = mutableSetOf<KCallable <* >>()
76
80
77
81
val excludeClasses = mutableSetOf<KClass <* >>()
78
82
79
83
val preserveClasses = mutableSetOf<KClass <* >>()
80
84
81
- val preserveProperties = mutableSetOf<KProperty <* >>()
85
+ val preserveProperties = mutableSetOf<KCallable <* >>()
82
86
83
87
fun clone (): TraverseConfiguration = TraverseConfiguration ().also {
84
88
it.excludeClasses.addAll(excludeClasses)
@@ -87,7 +91,7 @@ internal class CreateDataFrameDslImpl<T>(
87
91
it.preserveClasses.addAll(preserveClasses)
88
92
}
89
93
90
- override fun exclude (vararg properties : KProperty <* >) {
94
+ override fun exclude (vararg properties : KCallable <* >) {
91
95
excludeProperties.addAll(properties)
92
96
}
93
97
@@ -99,12 +103,12 @@ internal class CreateDataFrameDslImpl<T>(
99
103
preserveClasses.addAll(classes)
100
104
}
101
105
102
- override fun preserve (vararg properties : KProperty <* >) {
106
+ override fun preserve (vararg properties : KCallable <* >) {
103
107
preserveProperties.addAll(properties)
104
108
}
105
109
}
106
110
107
- override fun properties (vararg roots : KProperty <* >, maxDepth : Int , body : (TraversePropertiesDsl .() -> Unit )? ) {
111
+ override fun properties (vararg roots : KCallable <* >, maxDepth : Int , body : (TraversePropertiesDsl .() -> Unit )? ) {
108
112
val dsl = configuration.clone()
109
113
if (body != null ) {
110
114
body(dsl)
@@ -127,18 +131,47 @@ internal fun <T> Iterable<T>.createDataFrameImpl(clazz: KClass<*>, body: CreateD
127
131
internal fun convertToDataFrame (
128
132
data : Iterable <* >,
129
133
clazz : KClass <* >,
130
- roots : List <KProperty <* >>,
131
- excludes : Set <KProperty <* >>,
134
+ roots : List <KCallable <* >>,
135
+ excludes : Set <KCallable <* >>,
132
136
preserveClasses : Set <KClass <* >>,
133
- preserveProperties : Set <KProperty <* >>,
134
- maxDepth : Int
137
+ preserveProperties : Set <KCallable <* >>,
138
+ maxDepth : Int ,
135
139
): AnyFrame {
136
- val order = getPropertiesOrder(clazz)
140
+ val primaryConstructorOrder = getPropertyOrderFromPrimaryConstructor(clazz)
141
+ val anyConstructorOrder = getPropertyOrderFromAnyConstructor(clazz)
137
142
138
- val properties = roots.ifEmpty {
139
- clazz.memberProperties
140
- .filter { it.visibility == KVisibility .PUBLIC && it.parameters.toList().size == 1 }
141
- }.sortedBy { order[it.name] ? : Int .MAX_VALUE }
143
+ val properties: List <KCallable <* >> = roots
144
+ .ifEmpty {
145
+ clazz.memberProperties
146
+ .filter { it.visibility == KVisibility .PUBLIC && it.valueParameters.isEmpty() }
147
+ }
148
+
149
+ // fall back to getter functions for pojo-like classes
150
+ .ifEmpty {
151
+ clazz.memberFunctions
152
+ .filter {
153
+ it.visibility == KVisibility .PUBLIC &&
154
+ it.valueParameters.isEmpty() &&
155
+ (it.name.startsWith(" get" ) || it.name.startsWith(" is" ))
156
+ }
157
+ }
158
+
159
+ // sort properties by order of primary-ish constructor
160
+ .let {
161
+ val names = it.map { it.columnName }.toSet()
162
+
163
+ // use the primary constructor order if it's available,
164
+ // else try to find a constructor that matches all properties
165
+ val order = primaryConstructorOrder
166
+ ? : anyConstructorOrder.firstOrNull { map ->
167
+ names.all { it in map.keys }
168
+ }
169
+ if (order != null ) {
170
+ it.sortedBy { order[it.columnName] ? : Int .MAX_VALUE }
171
+ } else {
172
+ it
173
+ }
174
+ }
142
175
143
176
val columns = properties.mapNotNull {
144
177
val property = it
@@ -162,7 +195,7 @@ internal fun convertToDataFrame(
162
195
valueClassConverter
163
196
}
164
197
}
165
- property.javaField?.isAccessible = true
198
+ ( property as ? KProperty < * >)? .javaField?.isAccessible = true
166
199
property.isAccessible = true
167
200
168
201
var nullable = false
0 commit comments