diff options
Diffstat (limited to 'src/main/kotlin/me/bush/illnamethislater/EventBus.kt')
-rw-r--r-- | src/main/kotlin/me/bush/illnamethislater/EventBus.kt | 85 |
1 files changed, 56 insertions, 29 deletions
diff --git a/src/main/kotlin/me/bush/illnamethislater/EventBus.kt b/src/main/kotlin/me/bush/illnamethislater/EventBus.kt index 6b9b0bf..5b278e3 100644 --- a/src/main/kotlin/me/bush/illnamethislater/EventBus.kt +++ b/src/main/kotlin/me/bush/illnamethislater/EventBus.kt @@ -3,31 +3,32 @@ package me.bush.illnamethislater import kotlin.reflect.KClass /** - * A simple event dispatcher - * - * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) + * [A simple event dispatcher.](https://github.com/therealbush/eventbus-kotlin#tododothething) * * @author bush * @since 1.0.0 */ class EventBus(private val config: Config = Config()) { private val listeners = hashMapOf<KClass<*>, ListenerGroup>() - private val subscribers = mutableSetOf<Any>() + private val subscribers = hashMapOf<Any, List<Listener>>() /** - * doc + * Searches [subscriber] for members that return [Listener] and registers them. + * + * This will not find top level listeners, use [register] instead. + * + * Returns `false` if [subscriber] was already subscribed, `true` otherwise. * * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) */ fun subscribe(subscriber: Any): Boolean { return if (subscriber in subscribers) false else runCatching { - subscriber::class.listeners.forEach { member -> - register(member.handleCall(subscriber).also { - it.subscriber = subscriber - }) - } - subscribers.add(subscriber) + // Register every listener into a group, but also + // keep a separate list just for this subscriber. + subscribers[subscriber] = subscriber::class.listeners.map { member -> + register(member.handleCall(subscriber).also { it.subscriber = subscriber }) + }.toList() true }.getOrElse { config.logger.error("Unable to register listeners for subscriber $subscriber", it) @@ -36,40 +37,64 @@ class EventBus(private val config: Config = Config()) { } /** - * doc + * Unregisters all listeners belonging to [subscriber]. + * + * This will not remove top level listeners, use [unregister] instead. + * + * Returns `true` if [subscriber] was subscribed, `false` otherwise. * * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) */ fun unsubscribe(subscriber: Any): Boolean { - return subscribers.remove(subscriber).also { contains -> - if (contains) listeners.entries.removeIf { - it.value.unsubscribe(subscriber) - it.value.sequential.isEmpty() && it.value.parallel.isEmpty() - } + val contained = subscriber in subscribers + // Unregister every listener for this subscriber, + // and return null so the map entry is removed. + subscribers.computeIfPresent(subscriber) { _, listeners -> + listeners.forEach { unregister(it) } + null } + return contained } /** - * Registers a listener (which may not belong to any subscriber) to this [EventBus]. If no object - * is given, a key will be returned which can be used in [unsubscribe] to remove the listener. + * Registers a [Listener] to this [EventBus]. * * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) */ - fun register(listener: Listener) = listener.also { - listeners.computeIfAbsent(it.type) { type -> ListenerGroup(type, config) }.register(it) + fun register(listener: Listener): Listener { + listeners.computeIfAbsent(listener.type) { + ListenerGroup(it, config) + }.register(listener) + return listener } /** - * doc + * Unregisters a [Listener] from this [EventBus]. * * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) */ - fun unregister(listener: Listener) = listener.also { - listeners[it.type]?.unregister(it) + fun unregister(listener: Listener): Boolean { + return listeners[listener.type]?.let { + val contained = it.unregister(listener) + if (it.parallel.isEmpty() && it.sequential.isEmpty()) { + listeners.remove(listener.type) + } + contained + } ?: false } /** - * Posts an event. doc + * Posts an [event] to every listener that accepts its type. + * + * Events are **not** queued: only listeners subscribed currently will be called. + * + * If [event] is a subclass of [Event], or has a field-backed mutable boolean property + * named "cancelled" or "canceled" and [Config.thirdPartyCompatibility] is `true`, + * it can be cancelled by a listener, and only future listeners with [Listener.receiveCancelled] + * will receive it. + * + * Sequential listeners are called in the order of [Listener.priority], and parallel + * listeners are called before or after, depending on the value of [Config.parallelFirst]. * * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) */ @@ -79,13 +104,15 @@ class EventBus(private val config: Config = Config()) { * Logs the subscriber count, total listener count, and listener count for every event type with at * least one subscriber to [Config.logger]. Per-event counts are sorted from greatest to least listeners. * + * **This may cause a [ConcurrentModificationException] if [register] or [subscribe] is called in parallel.** + * * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething) * ``` * Subscribers: 5 - * Listeners: 8 sequential, 4 parallel - * BushIsSoCool: 4, 2 - * OtherEvent: 3, 1 - * String: 1, 1 + * Listeners: 8 sequential, 21 parallel + * BushIsSoCool: 4, 9 + * OtherEvent: 1, 10 + * String: 3, 0 */ fun debugInfo() { config.logger.info("Subscribers: ${subscribers.size}") |