Created
November 2, 2020 03:55
-
-
Save Exerosis/ef32fb37525c852c56373eacbd71ea85 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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