Introduce KIterablePropertyPath for type-safe property path mappings using collections.
See #3010 Original pull request: #3241 Signed-off-by: mipo256 <mikhailpolivakha@gmail.com>
This commit is contained in:
@@ -31,14 +31,29 @@ private class KPropertyPath<T, U>(
|
||||
val child: KProperty1<U, T>
|
||||
) : KProperty<T> by child
|
||||
|
||||
/**
|
||||
* Abstraction of a property path that consists of parent [KProperty],
|
||||
* and child property [KProperty], where parent [parent] has an [Iterable]
|
||||
* of children, so it represents 1-M mapping, not 1-1, like [KPropertyPath]
|
||||
*
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
internal class KIterablePropertyPath<T, U>(
|
||||
val parent: KProperty<Iterable<U?>?>,
|
||||
val child: KProperty1<U, T>
|
||||
) : KProperty<T> by child
|
||||
|
||||
/**
|
||||
* Recursively construct field name for a nested property.
|
||||
* @author Tjeu Kayim
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
internal fun asString(property: KProperty<*>): String {
|
||||
return when (property) {
|
||||
is KPropertyPath<*, *> ->
|
||||
"${asString(property.parent)}.${property.child.name}"
|
||||
is KIterablePropertyPath<*, *> ->
|
||||
"${asString(property.parent)}.${property.child.name}"
|
||||
else -> property.name
|
||||
}
|
||||
}
|
||||
@@ -55,5 +70,32 @@ internal fun asString(property: KProperty<*>): String {
|
||||
* @author Yoann de Martino
|
||||
* @since 2.5
|
||||
*/
|
||||
@JvmName("div")
|
||||
operator fun <T, U> KProperty<T?>.div(other: KProperty1<T, U>): KProperty<U> =
|
||||
KPropertyPath(this, other)
|
||||
|
||||
/**
|
||||
* Builds [KPropertyPath] from Property References.
|
||||
* Refer to a nested property in an embeddable or association.
|
||||
*
|
||||
* Note, that this function is different from [div] above in the
|
||||
* way that it represents a division operator overloading for
|
||||
* child references, where parent to child reference relation is 1-M, not 1-1.
|
||||
* It implies that parent has an [Iterable] or any liner [Collection] of children.
|
||||
**
|
||||
* For example, referring to the field "addresses.street":
|
||||
* ```
|
||||
* User::addresses / Author::street contains "Austin"
|
||||
* ```
|
||||
*
|
||||
* And the entities may look like this:
|
||||
* ```
|
||||
* class User(val addresses: List<Address>)
|
||||
*
|
||||
* class Address(val street: String)
|
||||
* ```
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
@JvmName("divIterable")
|
||||
operator fun <T, U> KProperty<Iterable<T?>?>.div(other: KProperty1<T, U>): KProperty<U> =
|
||||
KIterablePropertyPath(this, other)
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.junit.Test
|
||||
* @author Tjeu Kayim
|
||||
* @author Yoann de Martino
|
||||
* @author Mark Paluch
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
class KPropertyPathTests {
|
||||
|
||||
@@ -43,6 +44,14 @@ class KPropertyPathTests {
|
||||
assertThat(property).isEqualTo("author.name")
|
||||
}
|
||||
|
||||
@Test // DATACMNS-3010
|
||||
fun `Convert from Iterable nested KProperty to field name`() {
|
||||
|
||||
val property = (User::addresses / Address::street).toDotPath()
|
||||
|
||||
assertThat(property).isEqualTo("addresses.street")
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1835
|
||||
fun `Convert double nested KProperty to field name`() {
|
||||
|
||||
@@ -106,4 +115,8 @@ class KPropertyPathTests {
|
||||
|
||||
class Book(val title: String, val author: Author)
|
||||
class Author(val name: String)
|
||||
|
||||
class User(val addresses: List<Address>)
|
||||
|
||||
class Address(val street: String)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user