Skip to content

Instantly share code, notes, and snippets.

@Exerosis
Created November 2, 2020 03:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Exerosis/ef32fb37525c852c56373eacbd71ea85 to your computer and use it in GitHub Desktop.
Save Exerosis/ef32fb37525c852c56373eacbd71ea85 to your computer and use it in GitHub Desktop.
typealias Event<Listener> = Toggled.(Listener) -> (Unit)
open class TreeEvent<Listener> : (Toggled, Listener) -> (Unit), Iterable<Listener> {
protected val listeners = TreeMap<Int, Listener>()
protected var index = 0
override fun iterator() = listeners.values.iterator()
override fun invoke(scope: Toggled, listener: Listener) = scope.run {
val current = index++
onEnabled { listeners[current] = listener }
onDisabled { listeners -= current }
}
}
operator fun TreeEvent<() -> (Unit)>.invoke() = forEach { it() }
operator fun <First> TreeEvent<(First) -> (Unit)>.invoke(first: First) = forEach { it(first) }
operator fun <First, Second> TreeEvent<(First, Second) -> (Unit)>.invoke(first: First, second: Second) = forEach { it(first, second) }
operator fun <First, Second, Third> TreeEvent<(First, Second, Third) -> (Unit)>.invoke(first: First, second: Second, third: Third) = forEach { it(first, second, third) }
//TODO contract to imply that either to or from is not null.
typealias ChangeEvent<Key, Value> = Event<(Key, Value?, Value?) -> (Unit)>
//--Container--
interface Mutated<Key, Value> {
val onChanged: ChangeEvent<Key, Value>
operator fun get(key: Key): Value?
fun forEach(block: (Key, Value) -> (Unit))
}
interface Mutable<Key, Value> : Mutated<Key, Value> {
operator fun set(key: Key, value: Value?): Value?
companion object {
//TODO implement FRONT and BACK.
fun <Key, Value> fromMap(map: MutableMap<Key, Value>) = object : Mutable<Key, Value> {
override val onChanged = TreeEvent<(Key, Value?, Value?) -> (Unit)>()
override fun get(key: Key) = map[key]
override fun set(key: Key, value: Value?) = when (value) {
null -> map.remove(key)
else -> map.put(key, value)
}.also { onChanged(key, it, value) }
override fun forEach(block: (Key, Value) -> (Unit)) = map.forEach(block)
}
}
}
//--Table (map like)--
typealias Table<Key, Value> = Mutated<Key, Value>
typealias MutableTable<Key, Value> = Mutable<Key, Value>
val <Key, Value> Table<Key, Value>.onAdded get(): Event<(Key, Value) -> (Unit)> = {
onChanged { key, from, to -> if (from == null) it(key, to!!) }
}
val <Key, Value> Table<Key, Value>.onRemoved get(): Event<(Key, Value) -> (Unit)> = {
onChanged { key, from, to -> if (to == null) it(key, from!!) }
}
inline fun <Key, Value> Table<Key, Value>.watch(target: Key): Event<(Value?) -> (Unit)> = {
onEnabled { it(get(target)) }
onChanged { key, _, to -> if (target == key) it(to) }
}
inline operator fun <Value> Table<Value, *>.contains(value: Value) = get(value) != null
inline fun <Value> MutableTable<Value, *>.remove(value: Value) = set(value, null) != null
val <Key, Value> Mutated<Key, Value>.onEach get() : Toggled.(Toggled.(Key, Value) -> (Unit)) -> (Unit) = {
val components = HashMap<Key, Togglable>()
onChanged { key, from, to ->
if (from != null) components.remove(key)?.disable()
if (to != null) {
val component = Component { it(key, to) }
component.enable()
components[key] = component
}
}
onDisabled { components.values.removeIf { it.disable(); true } }
onEnabled {
forEach { key, value ->
val component = Component { it(key, value) }
component.enable()
components[key] = component
}
}
}
fun <Key, Value> Mutated<Key, Value>.filter(condition: (Key, Value) -> (Boolean)) = object : Mutated<Key, Value> {
override val onChanged: ChangeEvent<Key, Value> = { listener ->
this@filter.onChanged { key, from, to -> when {
from == null -> { if (condition(key, to!!)) listener(key, from, to) }
to == null -> { if (condition(key, from)) listener(key, from, to) }
condition(key, from) && !condition(key, to) -> listener(key, from, null)
!condition(key, from) && condition(key, to) -> listener(key, null, to)
condition(key, from) && condition(key, to) -> listener(key, from, to)
} }
}
override fun get(key: Key) = this@filter[key]?.takeIf { condition(key, it) }
override fun forEach(block: (Key, Value) -> Unit) = this@filter.forEach {
key, value -> if (condition(key, value)) block(key, value)
}
}
fun <Key, Value, To> MutableTable<Key, Value>.mapKeys(
from: (To) -> (Key), to: (Key) -> (To)
) = object : Mutable<To, Value> {
override val onChanged: ChangeEvent<To, Value> = {
this@mapKeys.onChanged { key, from, to ->
it(to(key), from, to)
}
}
override fun get(key: To) = this@mapKeys[from(key)]
override fun set(key: To, value: Value?) =
this@mapKeys.set(from(key), value)
override fun forEach(block: (To, Value) -> Unit) =
this@mapKeys.forEach { key, value -> block(to(key), value) }
}
fun <Key, Value, To> Mutated<Key, Value>.mapKeys(
from: (To) -> (Key), to: (Key) -> (To)
) = object : Mutated<To, Value> {
override val onChanged: ChangeEvent<To, Value> = {
this@mapKeys.onChanged { key, from, to ->
it(to(key), from, to)
}
}
override fun get(key: To) = this@mapKeys[from(key)]
override fun forEach(block: (To, Value) -> Unit) =
this@mapKeys.forEach { key, value -> block(to(key), value) }
}
fun <Key, Value, To> MutableTable<Key, Value>.mapValues(
from: (To) -> (Value), to: (Value) -> (To)
) = object : Mutable<Key, To> {
override val onChanged: ChangeEvent<Key, To> = {
this@mapValues.onChanged { key, from, to ->
it(key, from?.let { to(it) }, to?.let { to(it) })
}
}
override fun get(key: Key) = this@mapValues[key]?.let { to(it) }
override fun set(key: Key, value: To?) = set(key, value?.let { from(it) })?.let { to(it) }
override fun forEach(block: (Key, To) -> Unit) =
this@mapValues.forEach { key, value -> block(key, to(value)) }
}
fun <Key, Value, To> Mutated<Key, Value>.mapValues(
to: (Value) -> (To)
) = object : Mutated<Key, To> {
override val onChanged: ChangeEvent<Key, To> = {
this@mapValues.onChanged { key, from, to ->
it(key, from?.let { to(it) }, to?.let { to(it) })
}
}
override fun get(key: Key) = this@mapValues[key]?.let { to(it) }
override fun forEach(block: (Key, To) -> Unit) =
this@mapValues.forEach { key, value -> block(key, to(value)) }
}
//val <Key, Value> Mutated<Key, Value>.cache(map: MutableMap<Key, Value> = HashMap()) = object : Mutated<Key, Value> {
// override val onChanged = this@cache.onChanged
// override fun get(key: Key) = map[key]
// override fun forEach(block: (Key, Value) -> (Unit)) = map.forEach(block)
//
// init {
// onChanged {
//
// }
// }
//}
//--Holder (set like)--
typealias Holder<Value> = Mutated<Value, *>
typealias MutableHolder<Value> = Mutable<Value, Value>
inline fun <Value> MutableHolder<Value>.add(value: Value) = set(value, value) == null
inline val <Value> Holder<Value>.each get() : Toggled.(Toggled.(Value) -> (Unit)) -> (Unit) = {
onEach { key, _ -> it(key) }
}
inline fun <Key, Value, To> MutableTable<Key, Value>.map(
noinline from: (To) -> (Key), noinline to: (Key) -> (To)
) = mapKeys(from, to)
//FIXME figure out a somewhat better way of handling list likes.
//--Index (list like)--
typealias Index<Value> = Mutated<Int, Value>
typealias MutableIndex<Value> = Mutable<Int, Value>
val FRONT = Integer(Int.MAX_VALUE) as Int
val BACK = Integer(Int.MIN_VALUE) as Int
inline fun <Value> MutableIndex<Value>.pushBack(value: Value) = set(BACK, value)
inline fun <Value> MutableIndex<Value>.pushFront(value: Value) = set(FRONT, value)
inline fun <Value> MutableIndex<Value>.popBack() = set(BACK, null)
inline fun <Value> MutableIndex<Value>.popFront() = set(FRONT, null)
//FIXME doesn't support sparse arrays.
fun <Value> MutableIndex<Value>.push(index: Int, value: Value): Value? {
val previous = set(index, value)
if (previous != null) push(index + 1, previous)
return previous
}
interface Toggled {
val onEnabled: Event<() -> (Unit)>
val onDisabled: Event<() -> (Unit)>
operator fun <Return> (Toggled.() -> (Return)).invoke() = invoke(this@Toggled)
operator fun <Return, First> (Toggled.(First) -> (Return)).invoke(first: First) = invoke(this@Toggled, first)
operator fun <Return, First, Second> (Toggled.(First, Second) -> (Return)).invoke(first: First, second: Second) =
invoke(this@Toggled, first, second)
operator fun <Return, First, Second, Third> (Toggled.(First, Second, Third) -> (Return)).invoke(first: First, second: Second, third: Third) =
invoke(this@Toggled, first, second, third)
}
interface Togglable : Toggled {
var enabled: Boolean
}
inline fun Togglable.enable() { enabled = true }
inline fun Togglable.disable() { enabled = false }
fun Component(block: Togglable.() -> (Unit)): Togglable = object : Togglable {
val instance = this
override val onEnabled = object : TreeEvent<() -> (Unit)>() {
override fun invoke(scope: Toggled, listener: () -> Unit) {
if (scope === instance)
listeners[index++] = listener
else super.invoke(scope, listener)
}
}
override val onDisabled = object : TreeEvent<() -> (Unit)>() {
override fun invoke(scope: Toggled, listener: () -> Unit) {
if (scope === instance)
listeners[index++] = listener
else super.invoke(scope, listener)
}
}
override var enabled = false
set(enable) {
if (field == enable) return
if (enable) onEnabled()
else onDisabled()
field = enable
}
//TODO should we have this just in case? seems like a decent idea maybe.
init { getRuntime().addShutdownHook(Thread(::disable)); block(this) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment