Skip to content

Instantly share code, notes, and snippets.

@bnorm
Last active November 29, 2020 08:41
Show Gist options
  • Save bnorm/1ed41f509f143121e294535ddc752243 to your computer and use it in GitHub Desktop.
Save bnorm/1ed41f509f143121e294535ddc752243 to your computer and use it in GitHub Desktop.
One to many relationship via property delegation - Inspiration: https://github.com/jcornaz/xref
object OneToMany {
private class One<O, M, C : MutableCollection<M>>(initial: O?, private val rightProperty: KMutableProperty1<O, C>) : ReadWriteProperty<M, O?> {
var one: O? = initial
override fun getValue(thisRef: M, property: KProperty<*>): O? = one
override fun setValue(thisRef: M, property: KProperty<*>, value: O?) {
when {
value != null -> add(value, thisRef)
else -> one?.let { remove(it, thisRef) }
}
one = value
}
private fun add(one: O, m: M) {
(rightProperty.getDelegate(one) as Many<O, M, C>).many.add(m)
}
private fun remove(one: O, m: M) {
(rightProperty.getDelegate(one) as Many<O, M, C>).many.remove(m)
}
}
// todo(bnorm) - need to intercept add and remove events from the collection
private class Many<in O, M, C : MutableCollection<M>>(initial: C, private val leftProperty: KMutableProperty1<M, O?>) : ReadWriteProperty<O, C> {
var many = initial
override fun getValue(thisRef: O, property: KProperty<*>): C = many
override fun setValue(thisRef: O, property: KProperty<*>, value: C) {
for (m in many) set(m, null)
for (m in value) set(m, thisRef)
many = value
}
private fun set(many: M, one: O?) {
(leftProperty.getDelegate(many) as One<O, M, C>).one = one
}
}
fun <O, M, C : MutableCollection<M>> many(initial: C, leftProperty: KMutableProperty1<M, O?>): ReadWriteProperty<O, C> {
return Many(initial, leftProperty)
}
fun <O, M, C : MutableCollection<M>> one(initial: O?, rightProperty: KMutableProperty1<O, C>): ReadWriteProperty<M, O?> {
return One(initial, rightProperty)
}
}
class Parent {
var children: MutableSet<Child> by OneToMany.many(mutableSetOf(), Child::parent)
}
class Child {
var parent: Parent? by OneToMany.one(null, Parent::children)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment