mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 06:36:25 -04:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7767e68bc | ||
|
|
444a70e1fa | ||
|
|
8a58142b62 | ||
|
|
a108119420 | ||
|
|
6572478ee1 |
@@ -33,6 +33,7 @@ import java.io.PrintWriter
|
||||
* @since April 1st, 2022.
|
||||
*/
|
||||
object JsonManifestWalker {
|
||||
// im walkin ere
|
||||
/**
|
||||
* Builds a manifest at [manifestPath] using the files inside the same folder.
|
||||
*
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.CollectionSubscription
|
||||
|
||||
interface MutableObservableCollection<T, C : Collection<T>> :
|
||||
ObservableCollection<T, C>,
|
||||
MutableCollection<T> {
|
||||
|
||||
fun subscribe(
|
||||
priority: Priority = Priority.NORMAL,
|
||||
anyChangeHandler: (Pair<C, T>) -> Unit,
|
||||
additionHandler: ((Pair<C, T>) -> Unit)? = null,
|
||||
removalHandler: ((Pair<C, T>) -> Unit)? = null,
|
||||
): CollectionSubscription<T, C>
|
||||
|
||||
override fun add(element: T): Boolean
|
||||
|
||||
fun addIf(element: T, predicate: (C) -> Boolean): Boolean
|
||||
|
||||
override fun remove(element: T): Boolean
|
||||
|
||||
fun removeIf(predicate: (T) -> Boolean): Boolean
|
||||
|
||||
override fun addAll(elements: Collection<T>): Boolean
|
||||
|
||||
override fun retainAll(elements: Collection<T>): Boolean
|
||||
|
||||
override fun removeAll(elements: Collection<T>): Boolean
|
||||
|
||||
override fun iterator(): MutableIterator<T>
|
||||
|
||||
override fun clear()
|
||||
|
||||
fun mutableCopy(): MutableObservableCollection<T, C>
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections
|
||||
|
||||
interface MutableObservableCollectionIterator<T, C : Collection<T>, I : Iterator<T>> :
|
||||
ObservableCollectionIterator<T, I>,
|
||||
MutableIterator<T> {
|
||||
|
||||
override fun remove()
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
|
||||
interface ObservableCollection<T, C : Collection<T>> :
|
||||
Collection<T>,
|
||||
Observable<Pair<C, T>> {
|
||||
|
||||
val elements: C
|
||||
override val size: Int
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
handler: (Pair<C, T>) -> Unit,
|
||||
): ObservableSubscription<Pair<C, T>>
|
||||
|
||||
override fun unsubscribe(subscription: ObservableSubscription<Pair<C, T>>)
|
||||
|
||||
override fun isEmpty(): Boolean
|
||||
|
||||
fun isNotEmpty(): Boolean
|
||||
|
||||
override fun contains(element: T): Boolean
|
||||
|
||||
override fun containsAll(elements: Collection<T>): Boolean
|
||||
|
||||
override operator fun iterator(): Iterator<T>
|
||||
|
||||
fun copy(): ObservableCollection<T, C>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections
|
||||
|
||||
interface ObservableCollectionIterator<T, I : Iterator<T>> : Iterator<T> {
|
||||
|
||||
override fun hasNext(): Boolean
|
||||
override fun next(): T
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
|
||||
|
||||
/**
|
||||
* This attempts to cast [this] value to [Observable].
|
||||
*
|
||||
* @return An observable set with [this] as an [Observable] or an empty set.
|
||||
*/
|
||||
fun <T> T.getElementObservables(): Set<Observable<*>> {
|
||||
this.tryGetObservable()?.let { return setOf(it) }
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This attempts to cast [this] value to [Observable].
|
||||
*
|
||||
* @return This value as an [Observable] or null.
|
||||
*/
|
||||
fun <T> T.tryGetObservable(): Observable<*>? = if (this is Observable<*>) this else null
|
||||
|
||||
fun <T> MutableIterator<T>.removeIf(predicate: (T) -> Boolean): Boolean {
|
||||
var removed = false
|
||||
this.forEach { element ->
|
||||
if (predicate(element)) {
|
||||
this.remove()
|
||||
if (!removed) {
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.IndexedListSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.ListSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.removeIf
|
||||
|
||||
open class MutableObservableList<T>(
|
||||
list: Collection<T> = setOf(),
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) : MutableObservableCollection<T, List<T>>,
|
||||
ObservableList<T>(list, elementHandler),
|
||||
MutableList<T> {
|
||||
|
||||
private val additionObservable = SimpleObservable<Pair<List<T>, T>>()
|
||||
private val removalObservable = SimpleObservable<Pair<List<T>, T>>()
|
||||
private val setObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
|
||||
private val swapObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
|
||||
private val additionObservableIndexed = SimpleObservable<Triple<List<T>, T, Int>>()
|
||||
private val removalObservableIndexed = SimpleObservable<Triple<List<T>, T, Int>>()
|
||||
private val setObservableIndexed = SimpleObservable<Triple<List<T>, Pair<T, T>, Int>>()
|
||||
private val swapObservableIndexed = SimpleObservable<Triple<List<T>, Pair<T, T>, Pair<Int, Int>>>()
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
return ListSubscription(
|
||||
anyChange = this.subscribe(priority, anyChangeHandler),
|
||||
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
elements.forEach { anyChangeHandler(this to it) }
|
||||
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
|
||||
}
|
||||
|
||||
fun subscribe(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
@Suppress("DuplicatedCode")
|
||||
return ListSubscription(
|
||||
anyChange = this.subscribe(priority, anyChangeHandler),
|
||||
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
|
||||
set = setHandler?.let { setObservable.subscribe(priority, it) },
|
||||
swap = swapHandler?.let { swapObservable.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
elements.forEach { anyChangeHandler(this to it) }
|
||||
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler, setHandler, swapHandler)
|
||||
}
|
||||
|
||||
fun subscribeIndexed(
|
||||
priority: Priority,
|
||||
additionHandler: ((Triple<List<T>, T, Int>) -> Unit)?,
|
||||
removalHandler: ((Triple<List<T>, T, Int>) -> Unit)?,
|
||||
setHandler: ((Triple<List<T>, Pair<T, T>, Int>) -> Unit)?,
|
||||
swapHandler: ((Triple<List<T>, Pair<T, T>, Pair<Int, Int>>) -> Unit)?,
|
||||
): IndexedListSubscription<T> {
|
||||
return IndexedListSubscription(
|
||||
addition = additionHandler?.let { additionObservableIndexed.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservableIndexed.subscribe(priority, it) },
|
||||
set = setHandler?.let { setObservableIndexed.subscribe(priority, it) },
|
||||
swap = swapHandler?.let { swapObservableIndexed.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [register] & all subscriptions to [additionObservable],
|
||||
* [additionObservableIndexed], & [ObservableList.emitAnyChange].
|
||||
*/
|
||||
private fun emitAddition(element: T, index: Int): Boolean {
|
||||
register(element)
|
||||
additionObservable.emit(list to element)
|
||||
additionObservableIndexed.emit(Triple(list, element, index))
|
||||
return emitAnyChange(element)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [unregister] & all subscriptions to [removalObservable],
|
||||
* [removalObservableIndexed] & [ObservableList.emitAnyChange].
|
||||
*/
|
||||
private fun emitRemoval(element: T, index: Int): Boolean {
|
||||
unregister(element)
|
||||
removalObservable.emit(list to element)
|
||||
removalObservableIndexed.emit(Triple(list, element, index))
|
||||
return emitAnyChange(element)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [register], [unregister] & all subscriptions to [setObservable],
|
||||
* [setObservableIndexed], & [ObservableList.emitAnyChange].
|
||||
*/
|
||||
private fun emitSet(elements: Pair<T, T>, index: Int): Boolean {
|
||||
register(elements.first)
|
||||
unregister(elements.second)
|
||||
setObservable.emit(list to elements)
|
||||
setObservableIndexed.emit(Triple(list, elements, index))
|
||||
emitAnyChange(elements.first)
|
||||
emitAnyChange(elements.second)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling all subscriptions to [swapObservable],
|
||||
* [swapObservableIndexed], & [ObservableList.emitAnyChange].
|
||||
*/
|
||||
private fun emitSwap(elements: Pair<T, T>, indices: Pair<Int, Int>): Boolean {
|
||||
swapObservable.emit(list to elements)
|
||||
swapObservableIndexed.emit(Triple(list, elements, indices))
|
||||
emitAnyChange(elements.first)
|
||||
emitAnyChange(elements.second)
|
||||
return true
|
||||
}
|
||||
|
||||
override operator fun iterator() = MutableObservableListIterator(this)
|
||||
|
||||
override fun listIterator() = MutableObservableListIterator(this)
|
||||
|
||||
override fun listIterator(index: Int) = MutableObservableListIterator(this, index)
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): MutableObservableSubList<T> {
|
||||
return MutableObservableSubList(this, fromIndex, toIndex, elementHandler)
|
||||
}
|
||||
|
||||
override fun addIf(element: T, predicate: (List<T>) -> Boolean): Boolean {
|
||||
return if (predicate(list)) add(element) else false
|
||||
}
|
||||
|
||||
override fun add(element: T): Boolean {
|
||||
list.add(element)
|
||||
return emitAddition(element, lastIndex)
|
||||
}
|
||||
|
||||
fun addAtIf(index: Int, element: T, predicate: (List<T>) -> Boolean): Boolean {
|
||||
return if (predicate(list)) {
|
||||
add(index, element)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(index: Int, element: T) {
|
||||
list.add(index, element)
|
||||
emitAddition(element, index)
|
||||
}
|
||||
|
||||
fun setIf(index: Int, element: T, predicate: (T) -> Boolean): T? {
|
||||
return if (predicate(list[index])) set(index, element) else null
|
||||
}
|
||||
|
||||
override operator fun set(index: Int, element: T): T {
|
||||
return list.set(index, element).also { emitSet(element to it, index) }
|
||||
}
|
||||
|
||||
fun swapIf(indexOne: Int, indexTwo: Int, predicate: () -> Boolean): Boolean {
|
||||
return predicate().also { if (it) swap(indexOne, indexTwo) }
|
||||
}
|
||||
|
||||
fun swap(indexOne: Int, indexTwo: Int) {
|
||||
val one = list[indexOne]
|
||||
val two = list.set(indexTwo, one)
|
||||
list[indexOne] = two
|
||||
emitSwap(one to two, indexOne to indexTwo)
|
||||
}
|
||||
|
||||
override fun removeIf(predicate: (T) -> Boolean): Boolean = iterator().removeIf(predicate)
|
||||
|
||||
override fun remove(element: T): Boolean {
|
||||
val indexOf = list.indexOfFirst { it == element }
|
||||
if (indexOf == -1) {
|
||||
return false
|
||||
}
|
||||
list.removeAt(indexOf)
|
||||
return emitRemoval(element, indexOf)
|
||||
}
|
||||
|
||||
override fun removeAt(index: Int): T {
|
||||
val element = list.removeAt(index)
|
||||
emitRemoval(element, index)
|
||||
return element
|
||||
}
|
||||
|
||||
fun removeAtIf(index: Int, predicate: (T) -> Boolean): T? {
|
||||
val element = list[index]
|
||||
if (predicate(element)) {
|
||||
list.removeAt(index)
|
||||
emitRemoval(element, index)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<T>): Boolean {
|
||||
elements.forEach { add(it) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun addAll(index: Int, elements: Collection<T>): Boolean {
|
||||
var currentIndex = index
|
||||
elements.forEach { add(currentIndex++, it) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<T>):Boolean = removeIf { elements.contains(it) }
|
||||
|
||||
override fun retainAll(elements: Collection<T>):Boolean = removeIf { !elements.contains(it) }
|
||||
|
||||
override fun clear() {
|
||||
while (isNotEmpty()) {
|
||||
removeAt(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
// the name is not quite long enough yet...
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
|
||||
|
||||
open class MutableObservableListIterator<T>(
|
||||
observableList: MutableObservableList<T>,
|
||||
index: Int = 0,
|
||||
) : MutableObservableCollectionIterator<T, Set<T>, ListIterator<T>>,
|
||||
ObservableListIterator<T>(observableList, index),
|
||||
MutableListIterator<T> {
|
||||
|
||||
private val mutableList: MutableObservableList<T> = observableList
|
||||
|
||||
private var illegalSetOrRemoveState: Boolean? = null
|
||||
set (value) { if (field != value) field = value }
|
||||
|
||||
override fun next(): T {
|
||||
illegalSetOrRemoveState = false
|
||||
return super.next()
|
||||
}
|
||||
|
||||
override fun previous(): T {
|
||||
illegalSetOrRemoveState = false
|
||||
return super.previous()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
throwIfIllegalState("remove")
|
||||
mutableList.removeAt(--index)
|
||||
illegalSetOrRemoveState = true
|
||||
}
|
||||
|
||||
override fun set(element: T) {
|
||||
throwIfIllegalState("set")
|
||||
mutableList[index -1] = element
|
||||
}
|
||||
|
||||
override fun add(element: T) {
|
||||
when {
|
||||
mutableList.isEmpty() -> mutableList.add(element).run { index = 0 }
|
||||
index < mutableList.size -> mutableList.add(index++, element)
|
||||
else -> mutableList.add(element).run { index++ }
|
||||
}
|
||||
illegalSetOrRemoveState = true
|
||||
}
|
||||
|
||||
private fun throwIfIllegalState(function: String) {
|
||||
if (illegalSetOrRemoveState == null) {
|
||||
throw IllegalStateException(
|
||||
"Observable List iterator '$function' was called before 'next' or 'previous'."
|
||||
)
|
||||
} else if (illegalSetOrRemoveState == true) {
|
||||
throw IllegalStateException(
|
||||
"Observable List iterator '$function' was called, " +
|
||||
"but 'remove' or 'add' was already called this iteration."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.ListSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.removeIf
|
||||
|
||||
class MutableObservableSubList<T>(
|
||||
private val parentList: MutableObservableList<T>,
|
||||
private val fromIndex: Int,
|
||||
private val toIndex: Int,
|
||||
elementHandler: (T) -> Set<Observable<*>>,
|
||||
) : MutableObservableCollection<T, List<T>>,
|
||||
ObservableSubList<T>(parentList, fromIndex, toIndex, elementHandler),
|
||||
MutableList<T> {
|
||||
|
||||
private val additionObservable = SimpleObservable<Pair<List<T>, T>>()
|
||||
private val removalObservable = SimpleObservable<Pair<List<T>, T>>()
|
||||
private val setObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
|
||||
private val swapObservable = SimpleObservable<Pair<List<T>, Pair<T, T>>>()
|
||||
|
||||
override val elements: List<T>
|
||||
get() = when {
|
||||
isEmpty() -> emptyList()
|
||||
parentList.size < maxSize -> parentList.elements.subList(fromIndex, parentList.size)
|
||||
else -> parentList.elements.subList(fromIndex, currentToIndex)
|
||||
}
|
||||
private val maxLastIndex: Int = toIndex - 1
|
||||
private val maxSize: Int = toIndex - fromIndex
|
||||
override val size: Int get() = when {
|
||||
isEmpty() -> 0
|
||||
parentList.size < toIndex -> parentList.lastIndex - fromIndex
|
||||
else -> maxLastIndex
|
||||
}
|
||||
/** This is the last index in the sublist window, or -1 if the window is empty. */
|
||||
val currentLastIndex: Int get() = size - 1
|
||||
/** This is either [fromIndex] or -1 if the sublist window is empty. */
|
||||
val currentFromIndex: Int get() = if (isEmpty()) -1 else fromIndex
|
||||
/**
|
||||
* If [parentList] size > [toIndex] this is the toIndex,
|
||||
* if the parentList size is > [fromIndex] this is the parentList size,
|
||||
* or this is the [currentFromIndex].
|
||||
*/
|
||||
private val currentToIndex: Int
|
||||
get() = when {
|
||||
toIndex < parentList.size -> toIndex
|
||||
fromIndex < parentList.size -> parentList.size
|
||||
else -> currentFromIndex
|
||||
}
|
||||
private val currentLastIndexInParent: Int
|
||||
get() = when {
|
||||
isEmpty() -> fromIndex
|
||||
parentList.lastIndex < toIndex -> parentList.lastIndex
|
||||
else -> maxLastIndex
|
||||
}
|
||||
|
||||
init {
|
||||
parentList.subscribeIndexed(
|
||||
priority = Priority.NORMAL,
|
||||
additionHandler = { (_, element, index) -> emitAddition(element, index) },
|
||||
removalHandler = { (_, element, index) -> emitRemoval(element, index) },
|
||||
setHandler = { (_, elements, index) -> emitSet(elements, index) },
|
||||
swapHandler = { (_, element, index) -> emitSwap(element, index) },
|
||||
)
|
||||
}
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
return ListSubscription(
|
||||
anyChange = this.subscribe(priority, anyChangeHandler),
|
||||
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
elements.forEach { anyChangeHandler(this to it) }
|
||||
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
|
||||
}
|
||||
|
||||
fun subscribe(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
@Suppress("DuplicatedCode")
|
||||
return ListSubscription(
|
||||
anyChange = this.subscribe(priority, anyChangeHandler),
|
||||
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
|
||||
set = setHandler?.let { setObservable.subscribe(priority, it) },
|
||||
swap = swapHandler?.let { swapObservable.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<List<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<List<T>, T>) -> Unit)?,
|
||||
setHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
swapHandler: ((Pair<List<T>, Pair<T, T>>) -> Unit)?,
|
||||
): ListSubscription<T> {
|
||||
elements.forEach { anyChangeHandler(this to it) }
|
||||
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler, setHandler, swapHandler)
|
||||
}
|
||||
|
||||
private fun emitAddition(element: T, index: Int) {
|
||||
if (index >= toIndex) {
|
||||
return
|
||||
}
|
||||
val elements = this.elements
|
||||
if (index >= fromIndex) {
|
||||
handleAddition(elements, element)
|
||||
} else if(parentList.lastIndex >= fromIndex) {
|
||||
handleAddition(elements, parentList[fromIndex])
|
||||
}
|
||||
if (parentList.size > toIndex) {
|
||||
handleRemoval(elements, parentList[toIndex])
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitRemoval(element: T, index: Int) {
|
||||
if (index >= toIndex) {
|
||||
return
|
||||
}
|
||||
val elements = this.elements
|
||||
if (index >= fromIndex) {
|
||||
handleRemoval(elements, element)
|
||||
} else if(parentList.lastIndex >= fromIndex - 1) {
|
||||
handleRemoval(elements, parentList[fromIndex - 1])
|
||||
}
|
||||
if (currentLastIndexInParent >= toIndex - 1) {
|
||||
handleAddition(elements, parentList[toIndex - 1])
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitSet(elementPair: Pair<T, T>, index: Int) {
|
||||
if (!index.isInParentRelativeWindow()) {
|
||||
return
|
||||
}
|
||||
register(elementPair.first)
|
||||
unregister(elementPair.second)
|
||||
setObservable.emit(elements to elementPair)
|
||||
emitAnyChange(elementPair.first)
|
||||
emitAnyChange(elementPair.second)
|
||||
}
|
||||
|
||||
private fun emitSwap(elementPair: Pair<T, T>, indices: Pair<Int, Int>) {
|
||||
val firstInWindow = indices.first.isInParentRelativeWindow()
|
||||
val secondInWindow = indices.second.isInParentRelativeWindow()
|
||||
when {
|
||||
firstInWindow && !secondInWindow -> {
|
||||
val elements = this.elements
|
||||
handleRemoval(elements, elementPair.first)
|
||||
handleAddition(elements, elementPair.second)
|
||||
}
|
||||
!firstInWindow && secondInWindow -> {
|
||||
val elements = this.elements
|
||||
handleAddition(elements, elementPair.first)
|
||||
handleRemoval(elements, elementPair.second)
|
||||
}
|
||||
firstInWindow && secondInWindow -> {
|
||||
swapObservable.emit(elements to elementPair)
|
||||
emitAnyChange(elementPair.first)
|
||||
elementPair.second?.let { emitAnyChange(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddition(elements: List<T>, element: T) {
|
||||
register(element)
|
||||
additionObservable.emit(elements to element)
|
||||
emitAnyChange(element)
|
||||
}
|
||||
|
||||
private fun handleRemoval(elements: List<T>, element: T) {
|
||||
unregister(element)
|
||||
removalObservable.emit(elements to element)
|
||||
emitAnyChange(element)
|
||||
}
|
||||
|
||||
override operator fun iterator() = MutableObservableSubListIterator(this)
|
||||
|
||||
override fun listIterator() = MutableObservableSubListIterator(this)
|
||||
|
||||
override fun listIterator(index: Int) = MutableObservableSubListIterator(this, index)
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): MutableObservableSubList<T> {
|
||||
return parentList.subList(parentIndex(fromIndex), parentIndex(toIndex))
|
||||
}
|
||||
|
||||
fun swapIf(indexOne: Int, indexTwo: Int, predicate: () -> Boolean): Boolean {
|
||||
return predicate().also { if (it) swap(indexOne, indexTwo) }
|
||||
}
|
||||
|
||||
fun swap(indexOne: Int, indexTwo: Int) {
|
||||
val parentIndexOne = parentIndex(indexOne)
|
||||
val one = parentList[parentIndexOne]
|
||||
val two = parentList.set(parentIndex(indexTwo), one)
|
||||
parentList[parentIndexOne] = two
|
||||
}
|
||||
|
||||
override fun addIf(element: T, predicate: (List<T>) -> Boolean): Boolean {
|
||||
return if (predicate(this)) add(element) else false
|
||||
}
|
||||
|
||||
override fun add(element: T): Boolean {
|
||||
return if (parentList.lastIndex < toIndex) {
|
||||
parentList.add(element)
|
||||
} else {
|
||||
parentList.add(toIndex, element)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun addAtIf(index: Int, element: T, predicate: (List<T>) -> Boolean): Boolean {
|
||||
return if (predicate(this)) {
|
||||
add(index, element)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(index: Int, element: T) {
|
||||
parentList.add(parentIndex(index), element)
|
||||
}
|
||||
|
||||
override operator fun set(index: Int, element: T): T {
|
||||
return parentList.set(parentIndex(index), element)
|
||||
}
|
||||
|
||||
override fun removeIf(predicate: (T) -> Boolean): Boolean = iterator().removeIf(predicate)
|
||||
|
||||
override fun remove(element: T): Boolean {
|
||||
for (i in fromIndex until parentList.size) {
|
||||
val current = elements[i]
|
||||
if (current == element) {
|
||||
parentList.removeAt(i)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun removeAt(index: Int): T = parentList.removeAt(parentIndex(index))
|
||||
|
||||
override fun addAll(elements: Collection<T>): Boolean {
|
||||
val parentIndex = if (currentLastIndex > -1) currentLastIndex + fromIndex else fromIndex
|
||||
return if (parentIndex > parentList.lastIndex) {
|
||||
parentList.addAll(elements)
|
||||
} else {
|
||||
parentList.addAll(parentIndex, elements)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addAll(index: Int, elements: Collection<T>): Boolean {
|
||||
return parentList.addAll(parentIndex(index), elements)
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<T>): Boolean {
|
||||
return iterator().removeIf { elements.contains(it) }
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<T>): Boolean {
|
||||
return iterator().removeIf { !elements.contains(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation will remove all elements from the window dynamically. If an element
|
||||
* is removed & an element shifts into the sublist window that meets the predicate, the
|
||||
* new element will also be removed.
|
||||
*
|
||||
* Note: The other iterators including the ObservableSubListIterator will only operate on
|
||||
* the window provided before the iteration, even when the iterator is mutable.
|
||||
*/
|
||||
private fun removeAllIf(predicate: (T) -> Boolean): Boolean {
|
||||
if (isEmpty()) {
|
||||
return false
|
||||
}
|
||||
var mutated = false
|
||||
val outOfParentBounds: (Int) -> Boolean = { it == parentList.size }
|
||||
for (i in fromIndex until currentToIndex) {
|
||||
if (outOfParentBounds(i)) {
|
||||
return mutated
|
||||
}
|
||||
while (predicate(parentList[i])) {
|
||||
parentList.removeAt(i).also { if (!mutated) mutated = true }
|
||||
if (outOfParentBounds(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return mutated
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
if (isNotEmpty()) {
|
||||
iterator().removeIf { true }
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
// the name is not quite long enough yet...
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
|
||||
|
||||
open class MutableObservableSubListIterator<T>(
|
||||
subList: MutableObservableSubList<T>,
|
||||
index: Int = 0,
|
||||
) : MutableObservableCollectionIterator<T, List<T>, ListIterator<T>>,
|
||||
ObservableSubListIterator<T>(subList, index),
|
||||
MutableListIterator<T> {
|
||||
|
||||
private val mutableSubList: MutableObservableSubList<T> = subList
|
||||
|
||||
private var illegalSetOrRemoveState: Boolean? = null
|
||||
set (value) { if (field != value) field = value }
|
||||
|
||||
override fun next(): T {
|
||||
illegalSetOrRemoveState = false
|
||||
return super.next()
|
||||
}
|
||||
|
||||
override fun previous(): T {
|
||||
illegalSetOrRemoveState = false
|
||||
return super.previous()
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
throwIfIllegalState("remove")
|
||||
mutableSubList.removeAt(--index)
|
||||
illegalSetOrRemoveState = true
|
||||
}
|
||||
|
||||
override fun set(element: T) {
|
||||
throwIfIllegalState("set")
|
||||
mutableSubList[index -1] = element
|
||||
}
|
||||
|
||||
override fun add(element: T) {
|
||||
when {
|
||||
subList.isEmpty() -> {
|
||||
mutableSubList.add(element)
|
||||
index = mutableSubList.currentFromIndex
|
||||
}
|
||||
index < mutableSubList.currentLastIndex -> mutableSubList.add(index++, element)
|
||||
else -> mutableSubList.add(element).run { index++ }
|
||||
}
|
||||
illegalSetOrRemoveState = true
|
||||
}
|
||||
|
||||
private fun throwIfIllegalState(function: String) {
|
||||
if (illegalSetOrRemoveState == null) {
|
||||
throw IllegalStateException(
|
||||
"Observable Sub List iterator '$function' was called before 'next' or 'previous'."
|
||||
)
|
||||
} else if (illegalSetOrRemoveState == true) {
|
||||
throw IllegalStateException(
|
||||
"Observable Sub List iterator '$function' was called, " +
|
||||
"but 'remove' or 'add' was already called this iteration."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
|
||||
open class ObservableList<T>(
|
||||
list: Collection<T>,
|
||||
protected val elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) : ObservableCollection<T, List<T>>,
|
||||
List<T>,
|
||||
Observable<Pair<List<T>, T>> {
|
||||
|
||||
protected val list: MutableList<T> = list.toMutableList()
|
||||
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<List<T>, T>>>()
|
||||
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
|
||||
|
||||
override val elements: List<T> get() = list
|
||||
override val size: Int get() = list.size
|
||||
val lastIndex: Int get() = list.lastIndex
|
||||
|
||||
init {
|
||||
list.forEach { register(it) }
|
||||
}
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
handler: (Pair<List<T>, T>) -> Unit,
|
||||
): ObservableSubscription<Pair<List<T>, T>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
return subscription
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
handler: (Pair<List<T>, T>) -> Unit
|
||||
): ObservableSubscription<Pair<List<T>, T>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
list.forEach { handler(this to it) }
|
||||
return subscription
|
||||
}
|
||||
|
||||
override fun unsubscribe(subscription: ObservableSubscription<Pair<List<T>, T>>) {
|
||||
subscriptions.remove(subscription)
|
||||
}
|
||||
|
||||
protected fun register(element: T) {
|
||||
elementHandler(element).forEach{ observable ->
|
||||
subscriptionMap[observable] = observable.subscribe { emitAnyChange(element) }
|
||||
}
|
||||
}
|
||||
|
||||
protected fun unregister(element: T) {
|
||||
elementHandler(element).forEach{ subscriptionMap.remove(it)?.unsubscribe() }
|
||||
}
|
||||
|
||||
/** @return true after handling [subscriptions]. */
|
||||
protected fun emitAnyChange(element: T): Boolean {
|
||||
subscriptions.forEach { it.handle(list to element) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isEmpty() = list.isEmpty()
|
||||
|
||||
override fun isNotEmpty() = list.isNotEmpty()
|
||||
|
||||
override fun contains(element: T) = list.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<T>) = list.containsAll(elements)
|
||||
|
||||
override operator fun get(index: Int): T = list[index]
|
||||
|
||||
override fun indexOf(element: T) = list.indexOf(element)
|
||||
|
||||
override operator fun iterator(): Iterator<T> = list.iterator()
|
||||
|
||||
override fun lastIndexOf(element: T) = list.lastIndexOf(element)
|
||||
|
||||
override fun listIterator(): ListIterator<T> = list.listIterator()
|
||||
|
||||
override fun listIterator(index: Int): ListIterator<T> = list.listIterator(index = index)
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<T> = list.subList(fromIndex, toIndex)
|
||||
|
||||
override fun copy() = ObservableList(this.list, elementHandler)
|
||||
|
||||
open fun mutableCopy() = MutableObservableList(this.list, elementHandler)
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
open class ObservableListIterator<T>(
|
||||
protected var list: ObservableList<T>,
|
||||
protected var index: Int = 0,
|
||||
) : ListIterator<T> {
|
||||
|
||||
override fun hasNext(): Boolean = index < list.size
|
||||
|
||||
override fun hasPrevious(): Boolean = list.isNotEmpty() && index > 0
|
||||
|
||||
override fun next(): T {
|
||||
if (!hasNext()) {
|
||||
throw NoSuchElementException(
|
||||
"Observable List iterator does not have next element from index $index."
|
||||
)
|
||||
}
|
||||
return list[index++]
|
||||
}
|
||||
|
||||
override fun previous(): T {
|
||||
if (!hasPrevious()) {
|
||||
throw NoSuchElementException(
|
||||
"Observable List iterator does not have a previous element at index ${index -1}."
|
||||
)
|
||||
}
|
||||
return list[--index]
|
||||
}
|
||||
|
||||
override fun nextIndex(): Int = index
|
||||
|
||||
override fun previousIndex(): Int = index -1
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
|
||||
|
||||
fun <T> List<T>.saveToNbt(
|
||||
elementKey: String = "element",
|
||||
elementHandler: (T) -> NbtCompound,
|
||||
): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
var size = 0
|
||||
this.iterator().forEach { nbt.put(elementKey + size++, elementHandler(it)) }
|
||||
nbt.putInt("size", size)
|
||||
return nbt
|
||||
}
|
||||
|
||||
|
||||
fun <T> NbtCompound.loadObservableListOf(
|
||||
elementKey: String = "element",
|
||||
loadElementHandler: (NbtCompound) -> T,
|
||||
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = ObservableList(this.loadList(elementKey, loadElementHandler), elementObservableHandler)
|
||||
|
||||
|
||||
fun <T> NbtCompound.loadMutableObservableListOf(
|
||||
elementKey: String = "element",
|
||||
loadElementHandler: (NbtCompound) -> T,
|
||||
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = MutableObservableList(this.loadList(elementKey, loadElementHandler), elementObservableHandler)
|
||||
|
||||
|
||||
private fun <T> NbtCompound.loadList(
|
||||
elementKey: String = "element",
|
||||
elementHandler: (NbtCompound) -> T
|
||||
): List<T> {
|
||||
val newList = mutableListOf<T>()
|
||||
if (this.contains("size")) {
|
||||
val size = this.getInt("size")
|
||||
for (i in 0 until size) {
|
||||
newList.add(elementHandler(this.getCompound(elementKey + i)))
|
||||
}
|
||||
}
|
||||
return newList
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
|
||||
|
||||
fun <T> Collection<T>.toObservableList(
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = ObservableList(this, elementHandler)
|
||||
|
||||
|
||||
fun <T> Collection<T>.toMutableObservableList(
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = MutableObservableList(this, elementHandler)
|
||||
|
||||
|
||||
fun <T> observableListOf(
|
||||
vararg elements: T,
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): ObservableList<T> = ObservableList(elements.toList(), elementHandler)
|
||||
|
||||
|
||||
fun <T> mutableObservableListOf(
|
||||
vararg elements: T,
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): MutableObservableList<T> = MutableObservableList(elements.toList(), elementHandler)
|
||||
|
||||
|
||||
fun <T> observableListOf(
|
||||
elements: Collection<T> = setOf(),
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): ObservableList<T> = ObservableList(elements, elementHandler)
|
||||
|
||||
|
||||
fun <T> mutableObservableListOf(
|
||||
elements: Collection<T> = setOf(),
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): MutableObservableList<T> = MutableObservableList(elements, elementHandler)
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
|
||||
|
||||
open class ObservableSubList<T>(
|
||||
private val parentList: ObservableList<T>,
|
||||
private val fromIndex: Int,
|
||||
private val toIndex: Int,
|
||||
private val elementHandler: (T) -> Set<Observable<*>>,
|
||||
) : ObservableCollection<T, List<T>>,
|
||||
List<T>,
|
||||
Observable<Pair<List<T>, T>> {
|
||||
|
||||
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<List<T>, T>>>()
|
||||
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
|
||||
|
||||
override val elements: List<T> = parentList.elements.subList(fromIndex, toIndex)
|
||||
override val size: Int = toIndex - fromIndex
|
||||
|
||||
init {
|
||||
for (i in fromIndex until toIndex) {
|
||||
register(parentList[i])
|
||||
}
|
||||
}
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
handler: (Pair<List<T>, T>) -> Unit,
|
||||
): ObservableSubscription<Pair<List<T>, T>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
return subscription
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
handler: (Pair<List<T>, T>) -> Unit
|
||||
): ObservableSubscription<Pair<List<T>, T>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
elements.forEach { handler(this to it) }
|
||||
return subscription
|
||||
}
|
||||
|
||||
override fun unsubscribe(subscription: ObservableSubscription<Pair<List<T>, T>>) {
|
||||
subscriptions.remove(subscription)
|
||||
}
|
||||
|
||||
protected fun register(element: T) {
|
||||
elementHandler(element).forEach { observable ->
|
||||
subscriptionMap[observable] = observable.subscribe { emitAnyChange(element) }
|
||||
}
|
||||
}
|
||||
|
||||
protected fun unregister(element: T) {
|
||||
elementHandler(element).forEach { observable ->
|
||||
subscriptionMap.remove(observable)?.unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
protected fun emitAnyChange(element: T): Boolean {
|
||||
subscriptions.forEach { it.handle(elements to element) }
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun Int.isInParentRelativeWindow(): Boolean = this in fromIndex until toIndex
|
||||
|
||||
override fun isEmpty() = parentList.lastIndex < fromIndex
|
||||
|
||||
override fun isNotEmpty() = parentList.size > fromIndex
|
||||
|
||||
override fun contains(element: T) = elements.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<T>) = this.elements.containsAll(elements)
|
||||
|
||||
/** @throws IndexOutOfBoundsException if sublist does not contain [subListIndex]. */
|
||||
protected fun parentIndex(subListIndex: Int) = throwIfOutOfBounds(subListIndex) + fromIndex
|
||||
/**
|
||||
* @throws IndexOutOfBoundsException [index] is above [currentLastIndex] or below [fromIndex]
|
||||
* @return [index]
|
||||
*/
|
||||
private fun throwIfOutOfBounds(index: Int): Int {
|
||||
return if (index in 0 until size) {
|
||||
index
|
||||
} else {
|
||||
throw IndexOutOfBoundsException("Index $index is outside the sub list window.")
|
||||
}
|
||||
}
|
||||
|
||||
override operator fun get(index: Int): T {
|
||||
return parentList[parentIndex(index)]
|
||||
}
|
||||
|
||||
override fun indexOf(element: T): Int {
|
||||
val endIndex = fromIndex + size
|
||||
for (i in fromIndex until endIndex) {
|
||||
if (elements[i] == element) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: T): Int {
|
||||
val startIndex = fromIndex + size
|
||||
for (i in startIndex downTo fromIndex) {
|
||||
if (elements[i] == element) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
override operator fun iterator() = ObservableSubListIterator(this)
|
||||
|
||||
override fun listIterator() = ObservableSubListIterator(this)
|
||||
|
||||
override fun listIterator(index: Int) = ObservableSubListIterator(this, index)
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<T> {
|
||||
return parentList.subList(parentIndex(fromIndex), parentIndex(toIndex))
|
||||
}
|
||||
|
||||
/** @return a read-only copy that will NOT mutate if the backing list mutates. */
|
||||
override fun copy() = ObservableList(elements, elementHandler)
|
||||
|
||||
/** @return a mutable copy that will NOT mutate if the backing list mutates. */
|
||||
fun mutableCopy() = MutableObservableList(elements, elementHandler)
|
||||
|
||||
/** @return a read-only version of [parentList] that will NOT mutate if the parent list mutates */
|
||||
fun getParentCopy(): ObservableList<T> = parentList.copy()
|
||||
|
||||
/** @return a read-only version of [parentList] that will NOT mutate if the parent list mutates */
|
||||
fun getParentMutableCopy(): MutableObservableList<T> = parentList.mutableCopy()
|
||||
|
||||
/** @return a read-only version of [parentList] that WILL mutate if the backing list mutates */
|
||||
fun getParent(): ObservableList<T> = parentList
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.list
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollectionIterator
|
||||
|
||||
open class ObservableSubListIterator<T>(
|
||||
protected val subList: ObservableSubList<T>,
|
||||
protected var index: Int = 0,
|
||||
) : ObservableCollectionIterator<T, ListIterator<T>>,
|
||||
ListIterator<T> {
|
||||
|
||||
override fun hasNext(): Boolean = index < subList.size
|
||||
|
||||
override fun hasPrevious(): Boolean = subList.isNotEmpty() && index > 0
|
||||
|
||||
override fun next(): T {
|
||||
if (!hasNext()) {
|
||||
throw NoSuchElementException(
|
||||
"Observable Sub List iterator does not have next element from index $index."
|
||||
)
|
||||
}
|
||||
return subList[index++]
|
||||
}
|
||||
|
||||
override fun previous(): T {
|
||||
if (!hasPrevious()) {
|
||||
throw NoSuchElementException(
|
||||
"Observable Sub List iterator does not have previous element from index ${index - 1}."
|
||||
)
|
||||
}
|
||||
return subList[--index]
|
||||
}
|
||||
|
||||
override fun nextIndex(): Int = index
|
||||
|
||||
override fun previousIndex(): Int = index - 1
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.map
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.MapSubscription
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
class MutableObservableMap<K, V>(
|
||||
map: Map<K, V> = mapOf(),
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = { k, v -> k.getEntryObservables(v) },
|
||||
) : ObservableMap<K, V>(map = map, entryHandler = entryHandler),
|
||||
MutableMap<K, V> {
|
||||
|
||||
private val additionObservable = SimpleObservable<Pair<Map<K, V>, Pair<K, V>>>()
|
||||
private val removalObservable = SimpleObservable<Pair<Map<K, V>, Pair<K, V>>>()
|
||||
|
||||
fun subscribe(
|
||||
priority: Priority = Priority.NORMAL,
|
||||
anyChangeHandler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit,
|
||||
additionHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
|
||||
removalHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
|
||||
): MapSubscription<K, V> {
|
||||
return MapSubscription(
|
||||
anyChange = this.subscribe(priority, anyChangeHandler),
|
||||
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority = Priority.NORMAL,
|
||||
anyChangeHandler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit,
|
||||
additionHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
|
||||
removalHandler: ((Pair<Map<K, V>, Pair<K, V>>) -> Unit)? = null,
|
||||
): MapSubscription<K, V> {
|
||||
entries.forEach { anyChangeHandler(this to it.pair()) }
|
||||
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [register] & all subscriptions to [additionObservable],
|
||||
* & [ObservableMap.emitAnyChange].
|
||||
*/
|
||||
private fun emitAddition(key: K, value: V): Boolean {
|
||||
register(key, value)
|
||||
additionObservable.emit(map to (key to value))
|
||||
return emitAnyChange(key, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [unregister] & all subscriptions to [removalObservable],
|
||||
* & [ObservableMap.emitAnyChange].
|
||||
*/
|
||||
private fun emitRemoval(key: K, value: V): Boolean {
|
||||
unregister(key, value)
|
||||
removalObservable.emit(map to (key to value))
|
||||
return emitAnyChange(key, value)
|
||||
}
|
||||
|
||||
override operator fun iterator(): MutableObservableMapIterator<K, V> {
|
||||
return MutableObservableMapIterator(this)
|
||||
}
|
||||
|
||||
operator fun set(key: K, value: V): V? = put(key, value)
|
||||
|
||||
fun put(pair: Pair<K, V>): V? = put(pair.first, pair.second)
|
||||
|
||||
fun put(entry: Entry<K, V>): V? = put(entry.key, entry.value)
|
||||
|
||||
override fun put(key: K, value: V): V? {
|
||||
val previous = map.put(key, value)
|
||||
emitAddition(key, value)
|
||||
if (previous != null) {
|
||||
emitRemoval(key, previous)
|
||||
}
|
||||
return previous
|
||||
}
|
||||
|
||||
fun remove(key: K, value: V): Boolean {
|
||||
return if (map[key] == value) (remove(key) != null) else false
|
||||
}
|
||||
|
||||
override fun remove(key: K): V? {
|
||||
val value = map.remove(key) ?: return null
|
||||
emitRemoval(key, value)
|
||||
return value
|
||||
}
|
||||
|
||||
override fun putAll(from: Map<out K, V>) = from.forEach { put(it.key, it.value) }
|
||||
|
||||
fun getOrDefault(key: K, defaultValue: V): V = map[key] ?: defaultValue
|
||||
|
||||
fun getOrPut(key: K, newValue: V) = map[key] ?: newValue.also { put(key, it) }
|
||||
|
||||
override fun clear() {
|
||||
if (map.isNotEmpty()) {
|
||||
keys.toSet().forEach { remove(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.map
|
||||
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
open class MutableObservableMapIterator<K, V>(
|
||||
private val observableMap: MutableObservableMap<K, V>,
|
||||
) : ObservableMapIterator<K, V>(observableMap.entries.toSet().iterator()),
|
||||
MutableIterator<Entry<K, V>> {
|
||||
|
||||
private lateinit var currentEntry: Entry<K, V>
|
||||
|
||||
private var removedThisIteration: Boolean? = null
|
||||
set (value) { if (field != value) field = value }
|
||||
|
||||
override fun next(): Entry<K, V> {
|
||||
currentEntry = super.next()
|
||||
removedThisIteration = false
|
||||
return currentEntry
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
if (removedThisIteration == null) {
|
||||
throw IllegalStateException(
|
||||
"Observable Map iterator 'remove' was called before 'next' or 'previous'."
|
||||
)
|
||||
} else if (removedThisIteration == true) {
|
||||
throw IllegalStateException(
|
||||
"Observable Map iterator 'remove' was called, " +
|
||||
"but 'remove' was already called this iteration."
|
||||
)
|
||||
}
|
||||
observableMap.remove(currentEntry.key)
|
||||
removedThisIteration = true
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.map
|
||||
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
open class ObservableMap<K, V>(
|
||||
map: Map<K, V>,
|
||||
protected val entryHandler: (K, V) -> Set<Observable<*>> =
|
||||
{ k: K, v: V -> k.getEntryObservables(v) },
|
||||
) : Map<K, V>,
|
||||
Observable<Pair<Map<K, V>, Pair<K, V>>> {
|
||||
|
||||
protected val map: MutableMap<K, V> = map.toMutableMap()
|
||||
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>>>()
|
||||
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
|
||||
|
||||
override val size get() = map.size
|
||||
override val keys get() = map.keys
|
||||
override val values get() = map.values
|
||||
override val entries get() = map.entries
|
||||
|
||||
init {
|
||||
this.map.forEach { (key, value) ->
|
||||
entryHandler(key, value).forEach{ observable ->
|
||||
subscriptionMap[observable] = observable.subscribe {
|
||||
emitAnyChange(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
handler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit
|
||||
): ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
return subscription
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
handler: (Pair<Map<K, V>, Pair<K, V>>) -> Unit
|
||||
): ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
map.forEach { handler(this to (it.pair())) }
|
||||
return subscription
|
||||
}
|
||||
|
||||
override fun unsubscribe(subscription: ObservableSubscription<Pair<Map<K, V>, Pair<K, V>>>) {
|
||||
subscriptions.remove(subscription)
|
||||
}
|
||||
|
||||
protected open fun register(key: K, value: V) {
|
||||
if (contains(key, value)) {
|
||||
entryHandler(key, value).forEach{ observable ->
|
||||
subscriptionMap[observable] = observable.subscribe { emitAnyChange(key, value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun unregister(key: K, value: V) {
|
||||
entryHandler(key, value).forEach{ subscriptionMap.remove(it)?.unsubscribe() }
|
||||
}
|
||||
|
||||
protected fun emitAnyChange(key: K, value: V): Boolean {
|
||||
subscriptions.forEach { it.handle(map to (key to value)) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isEmpty() = map.isEmpty()
|
||||
|
||||
fun isNotEmpty() = map.isNotEmpty()
|
||||
|
||||
override fun containsKey(key: K) = map.containsKey(key)
|
||||
|
||||
override fun containsValue(value: V) = map.containsValue(value)
|
||||
|
||||
fun contains(pair: Pair<K, V>): Boolean = contains(pair.first, pair.second)
|
||||
|
||||
fun contains(entry: Entry<K, V>): Boolean = contains(entry.key, entry.value)
|
||||
|
||||
fun contains(key: K, value: V) = map[key] == value
|
||||
|
||||
fun contains(predicate: (Entry<K, V>) -> Boolean) = map.any(predicate)
|
||||
|
||||
fun containsAll(other: Collection<Entry<K, V>>): Boolean {
|
||||
return map.all { entry -> other.any { it.key == entry.key && it.value == entry.value } }
|
||||
}
|
||||
|
||||
fun containsAll(other: Map<K, V>): Boolean {
|
||||
return map.all { entry -> other.any { it.key == entry.key && it.value == entry.value } }
|
||||
}
|
||||
|
||||
open operator fun iterator(): Iterator<Entry<K, V>> = map.iterator()
|
||||
|
||||
override operator fun get(key: K) = map[key]
|
||||
|
||||
fun toSortedMap(comparator: Comparator<in K>) = map.toMap().toSortedMap(comparator)
|
||||
|
||||
fun firstKeyOrNull(predicate: (Entry<K, V>) -> Boolean = { true }): K? {
|
||||
map.forEach { if (predicate(it)) return it.key }
|
||||
return null
|
||||
}
|
||||
|
||||
fun firstValueOrNull(predicate: (Entry<K, V>) -> Boolean = { true }): V? {
|
||||
map.forEach { if (predicate(it)) return it.value }
|
||||
return null
|
||||
}
|
||||
|
||||
fun firstEntryOrNull(predicate: (Entry<K, V>) -> Boolean = { true }): Entry<K, V>? {
|
||||
map.forEach { if (predicate(it)) return it }
|
||||
return null
|
||||
}
|
||||
|
||||
fun copy() = ObservableMap(this.map, entryHandler)
|
||||
|
||||
fun mutableCopy() = MutableObservableMap(this.map, entryHandler)
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.map
|
||||
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
open class ObservableMapIterator<K, V>(
|
||||
protected var iterator: Iterator<Entry<K, V>>
|
||||
) : Iterator<Entry<K, V>> {
|
||||
|
||||
override fun hasNext():Boolean = iterator.hasNext()
|
||||
override fun next(): Entry<K, V> = iterator.next()
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.map
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
|
||||
fun <K, V> Map<K, V>.saveToNbt(
|
||||
entryKey: String = "entry",
|
||||
entryHandler: (Entry<K, V>) -> NbtCompound,
|
||||
): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
var size = 0
|
||||
this.iterator().forEach { nbt.put(entryKey + size++, entryHandler(it)) }
|
||||
nbt.putInt("size", size)
|
||||
return nbt
|
||||
}
|
||||
|
||||
|
||||
fun <K, V> NbtCompound.loadObservableMapOf(
|
||||
entryKey: String = "entry",
|
||||
loadEntryHandler: (NbtCompound) -> Pair<K, V>,
|
||||
entryObservableHandler: (K, V) -> Set<Observable<*>> = { k, v -> k.getEntryObservables(v) },
|
||||
) = ObservableMap(this.loadMap(entryKey, loadEntryHandler), entryObservableHandler)
|
||||
|
||||
|
||||
fun <K, V> NbtCompound.loadMutableObservableMapOf(
|
||||
entryKey: String = "entry",
|
||||
loadEntryHandler: (NbtCompound) -> Pair<K, V>,
|
||||
entryObservableHandler: (K, V) -> Set<Observable<*>> = { k, v -> k.getEntryObservables(v) },
|
||||
) = MutableObservableMap(this.loadMap(entryKey, loadEntryHandler), entryObservableHandler)
|
||||
|
||||
|
||||
private fun <K, V> NbtCompound.loadMap(
|
||||
entryKey: String = "entry",
|
||||
entryHandler: (NbtCompound) -> Pair<K, V>,
|
||||
): MutableMap<K, V> {
|
||||
val newMap: MutableMap<K, V> = mutableMapOf()
|
||||
if (this.contains("size")) {
|
||||
val size = this.getInt("size")
|
||||
for (i in 0 until size) {
|
||||
val (key, value) = entryHandler(this.getCompound(entryKey + i))
|
||||
newMap[key] = value
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.map
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.tryGetObservable
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
|
||||
fun <K, V> Entry<K, V>.pair() = this.key to this.value
|
||||
|
||||
|
||||
fun <K, V> Collection<Entry<K, V>>.pairs(): Set<Pair<K, V>> {
|
||||
mutableSetOf<Pair<K, V>>().let { set ->
|
||||
this.forEach { set.add(it.key to it.value) }
|
||||
return set
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <K, V> Map<K, V>.pairs(): Set<Pair<K, V>> {
|
||||
mutableSetOf<Pair<K, V>>().let { set ->
|
||||
this.forEach { set.add(it.key to it.value) }
|
||||
return set
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <K, V> Collection<Entry<K, V>>.toMap(): Map<K, V> = this.toMutableMap()
|
||||
|
||||
|
||||
fun <K, V> Collection<Entry<K, V>>.toMutableMap(): MutableMap<K, V> {
|
||||
return mutableMapOf<K, V>().putAll(this)
|
||||
}
|
||||
|
||||
|
||||
fun <K, V> MutableMap<K, V>.putAll(entries: Collection<Entry<K, V>>): MutableMap<K, V> {
|
||||
return entries.forEach { this[it.key] = it.value }.let { this }
|
||||
}
|
||||
|
||||
|
||||
fun <K, V> defaultEntryHandler() = { k: K, v: V -> k.getEntryObservables(v) }
|
||||
|
||||
|
||||
fun <K, V> K.getEntryObservables(value: V): Set<Observable<*>> {
|
||||
val observables = mutableSetOf<Observable<*>>()
|
||||
this.tryGetObservable()?.let { observables.add(it) }
|
||||
value.tryGetObservable()?.let { observables.add(it) }
|
||||
return observables
|
||||
}
|
||||
|
||||
|
||||
fun <K, V> Map<K, V>.toObservableMap(
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(this, entryHandler)
|
||||
|
||||
fun <K, V> Map<K, V>.toMutableObservableMap(
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(this, entryHandler)
|
||||
|
||||
fun <K, V> Collection<Pair<K, V>>.toObservableMap(
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(this.toMap(), entryHandler)
|
||||
|
||||
fun <K, V> Collection<Pair<K, V>>.toMutableObservableMap(
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(this.toMap(), entryHandler)
|
||||
|
||||
fun <K, V> observableMapOf(
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(mapOf(), entryHandler)
|
||||
|
||||
fun <K, V> mutableObservableMapOf(
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(mapOf(), entryHandler)
|
||||
|
||||
fun <K, V> observableMapOf(
|
||||
map: Map<K, V> = emptyMap(),
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(map, entryHandler)
|
||||
|
||||
fun <K, V> observableMapOf(
|
||||
vararg pairs: Pair<K, V>,
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(pairs.toMap(), entryHandler)
|
||||
|
||||
fun <K, V> mutableObservableMapOf(
|
||||
vararg pairs: Pair<K, V>,
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(pairs.toMap(), entryHandler)
|
||||
|
||||
fun <K, V> mutableObservableMapOf(
|
||||
map: Map<K, V> = emptyMap(),
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(map, entryHandler)
|
||||
|
||||
fun <K, V> observableMapOf(
|
||||
vararg entries: Entry<K, V>,
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(entries.toSet().toMap(), entryHandler)
|
||||
|
||||
fun <K, V> mutableObservableMapOf(
|
||||
vararg entries: Entry<K, V>,
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(entries.toSet().toMap(), entryHandler)
|
||||
|
||||
fun <K, V> observableMapOf(
|
||||
pairs: Collection<Pair<K, V>> = emptySet(),
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = ObservableMap(pairs.toMap(), entryHandler)
|
||||
|
||||
fun <K, V> mutableObservableMapOf(
|
||||
pairs: Collection<Pair<K, V>> = emptySet(),
|
||||
entryHandler: (K, V) -> Set<Observable<*>> = defaultEntryHandler(),
|
||||
) = MutableObservableMap(pairs.toMap(), entryHandler)
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.set
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
import com.cobblemon.mod.common.api.reactive.collections.subscriptions.SetSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.removeIf
|
||||
|
||||
class MutableObservableSet<T>(
|
||||
set: Collection<T> = setOf(),
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) : MutableObservableCollection<T, Set<T>>,
|
||||
ObservableSet<T>(set, elementHandler),
|
||||
MutableSet<T> {
|
||||
|
||||
private val additionObservable = SimpleObservable<Pair<Set<T>, T>>()
|
||||
private val removalObservable = SimpleObservable<Pair<Set<T>, T>>()
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<Set<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<Set<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<Set<T>, T>) -> Unit)?,
|
||||
): SetSubscription<T> {
|
||||
return SetSubscription(
|
||||
anyChange = this.subscribe(priority, anyChangeHandler),
|
||||
addition = additionHandler?.let { additionObservable.subscribe(priority, it) },
|
||||
removal = removalHandler?.let { removalObservable.subscribe(priority, it) },
|
||||
)
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
anyChangeHandler: (Pair<Set<T>, T>) -> Unit,
|
||||
additionHandler: ((Pair<Set<T>, T>) -> Unit)?,
|
||||
removalHandler: ((Pair<Set<T>, T>) -> Unit)?,
|
||||
): SetSubscription<T> {
|
||||
elements.forEach { anyChangeHandler(this to it) }
|
||||
return subscribe(priority, anyChangeHandler, additionHandler, removalHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [register] & all subscriptions to [additionObservable]
|
||||
* & [ObservableSet.emitAnyChange].
|
||||
*/
|
||||
private fun emitAddition(element: T): Boolean {
|
||||
register(element)
|
||||
additionObservable.emit(set to element)
|
||||
return emitAnyChange(element)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` after handling [unregister] & all subscriptions to [removalObservable]
|
||||
* & [ObservableSet.emitAnyChange].
|
||||
*/
|
||||
private fun emitRemoval(element: T): Boolean {
|
||||
unregister(element)
|
||||
removalObservable.emit(set to element)
|
||||
return emitAnyChange(element)
|
||||
}
|
||||
|
||||
override operator fun iterator() = MutableObservableSetIterator(this)
|
||||
|
||||
override fun addIf(element: T, predicate: (Set<T>) -> Boolean): Boolean {
|
||||
return if (predicate(set)) add(element) else false
|
||||
}
|
||||
|
||||
override fun add(element: T): Boolean {
|
||||
return if (set.add(element)) emitAddition(element) else false
|
||||
}
|
||||
|
||||
override fun removeIf(predicate: (T) -> Boolean): Boolean {
|
||||
return iterator().removeIf(predicate)
|
||||
}
|
||||
|
||||
fun removeIf(element: T, predicate: (Set<T>) -> Boolean): Boolean {
|
||||
return if (predicate(set)) remove(element) else false
|
||||
}
|
||||
|
||||
override fun remove(element: T): Boolean {
|
||||
return if (set.remove(element)) emitRemoval(element) else false
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<T>): Boolean {
|
||||
var mutated = false
|
||||
elements.forEach { if (add(it) && !mutated) mutated = true }
|
||||
return mutated
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<T>):Boolean = removeIf { elements.contains(it) }
|
||||
|
||||
override fun retainAll(elements: Collection<T>):Boolean = removeIf { !elements.contains(it) }
|
||||
|
||||
override fun clear() {
|
||||
// just a simpler way to call the mutable iterator & emit with each removal
|
||||
removeIf { true }
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.set
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollectionIterator
|
||||
|
||||
open class MutableObservableSetIterator<T>(
|
||||
private val observableSet: MutableObservableSet<T>,
|
||||
) : MutableObservableCollectionIterator<T, Set<T>, Iterator<T>>,
|
||||
ObservableSetIterator<T>(observableSet.elements.toSet().iterator()),
|
||||
MutableIterator<T> {
|
||||
|
||||
private var currentElement: T? = null
|
||||
set (value) { if (field != value) field = value }
|
||||
|
||||
private var removedThisIteration: Boolean = false
|
||||
set (value) { if (field != value) field = value }
|
||||
|
||||
override fun next(): T {
|
||||
currentElement = iterator.next()
|
||||
removedThisIteration = false
|
||||
return currentElement!!
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
if (currentElement == null) {
|
||||
throw IllegalStateException("Observable Set iterator 'remove' was called before 'next' or 'previous'.")
|
||||
} else if (removedThisIteration) {
|
||||
throw IllegalStateException("Observable Set iterator 'remove' was called, but 'remove' was already called this iteration.")
|
||||
}
|
||||
observableSet.remove(currentElement)
|
||||
currentElement = null
|
||||
removedThisIteration = true
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.set
|
||||
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
|
||||
open class ObservableSet<T>(
|
||||
set: Collection<T>,
|
||||
protected val elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) : ObservableCollection<T, Set<T>>,
|
||||
Set<T>,
|
||||
Observable<Pair<Set<T>, T>> {
|
||||
|
||||
protected val set: MutableSet<T> = set.toMutableSet()
|
||||
private val subscriptions = PrioritizedList<ObservableSubscription<Pair<Set<T>, T>>>()
|
||||
private val subscriptionMap: MutableMap<Observable<*>, ObservableSubscription<*>> = mutableMapOf()
|
||||
|
||||
// Keep as 'this.set' & not 'set,' b/c compiler doesn't like it & cries a little.
|
||||
override val elements: Set<T> get() = this.set
|
||||
override val size: Int get() = set.size
|
||||
|
||||
init {
|
||||
set.forEach { register(it) }
|
||||
}
|
||||
|
||||
override fun subscribe(
|
||||
priority: Priority,
|
||||
handler: (Pair<Set<T>, T>) -> Unit,
|
||||
): ObservableSubscription<Pair<Set<T>, T>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
return subscription
|
||||
}
|
||||
|
||||
fun subscribeAndHandle(
|
||||
priority: Priority,
|
||||
handler: (Pair<Set<T>, T>) -> Unit
|
||||
): ObservableSubscription<Pair<Set<T>, T>> {
|
||||
val subscription = ObservableSubscription(this, handler)
|
||||
subscriptions.add(priority, subscription)
|
||||
set.forEach { handler(this to it) }
|
||||
return subscription
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: fix issue where passing subscription to addition or removal does not unsubscribe
|
||||
Does this issue really matter?
|
||||
Calling unsubscribe from the subscription directly works as intended
|
||||
*/
|
||||
override fun unsubscribe(subscription: ObservableSubscription<Pair<Set<T>, T>>) {
|
||||
subscriptions.remove(subscription)
|
||||
}
|
||||
|
||||
protected fun register(element: T) {
|
||||
elementHandler(element).forEach{ observable ->
|
||||
subscriptionMap[observable] = observable.subscribe { emitAnyChange(element) }
|
||||
}
|
||||
}
|
||||
|
||||
protected fun unregister(element: T) {
|
||||
elementHandler(element).forEach{ subscriptionMap.remove(it)?.unsubscribe() }
|
||||
}
|
||||
|
||||
/** @return true after handling [subscriptions]. */
|
||||
protected fun emitAnyChange(element: T): Boolean {
|
||||
subscriptions.forEach { it.handle(set to element) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isEmpty() = set.isEmpty()
|
||||
|
||||
override fun isNotEmpty() = set.isNotEmpty()
|
||||
|
||||
override fun contains(element: T) = set.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<T>) = set.containsAll(elements)
|
||||
|
||||
override operator fun iterator(): Iterator<T> = set.iterator()
|
||||
|
||||
override fun copy() = ObservableSet(set, elementHandler)
|
||||
|
||||
fun mutableCopy() = MutableObservableSet(set, elementHandler)
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.set
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollectionIterator
|
||||
|
||||
open class ObservableSetIterator<T>(
|
||||
protected var iterator: Iterator<T>
|
||||
) : ObservableCollectionIterator<T, Iterator<T>> {
|
||||
|
||||
override fun hasNext():Boolean = iterator.hasNext()
|
||||
override fun next(): T = iterator.next()
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.set
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
|
||||
|
||||
fun <T> Set<T>.saveToNbt(
|
||||
elementKey: String = "element",
|
||||
elementHandler: (T) -> NbtCompound,
|
||||
): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
var size = 0
|
||||
this.iterator().forEach { nbt.put(elementKey + size++, elementHandler(it)) }
|
||||
nbt.putInt("size", size)
|
||||
return nbt
|
||||
}
|
||||
|
||||
|
||||
fun <T> NbtCompound.loadObservableSetOf(
|
||||
elementKey: String = "element",
|
||||
loadElementHandler: (NbtCompound) -> T,
|
||||
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = ObservableSet(this.loadSet(elementKey, loadElementHandler), elementObservableHandler)
|
||||
|
||||
|
||||
fun <T> NbtCompound.loadMutableObservableSetOf(
|
||||
elementKey: String = "element",
|
||||
loadElementHandler: (NbtCompound) -> T,
|
||||
elementObservableHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = MutableObservableSet(this.loadSet(elementKey, loadElementHandler), elementObservableHandler)
|
||||
|
||||
|
||||
private fun <T> NbtCompound.loadSet(
|
||||
elementKey: String = "element",
|
||||
elementHandler: (NbtCompound) -> T,
|
||||
): Set<T> {
|
||||
val newSet = mutableSetOf<T>()
|
||||
if (this.contains("size")) {
|
||||
val size = this.getInt("size")
|
||||
for (i in 0 until size) {
|
||||
newSet.add(elementHandler(this.getCompound(elementKey + i)))
|
||||
}
|
||||
}
|
||||
return newSet
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.set
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.collections.getElementObservables
|
||||
|
||||
|
||||
fun <T> Collection<T>.toObservableSet(
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = ObservableSet(this, elementHandler)
|
||||
|
||||
|
||||
fun <T> Collection<T>.toMutableObservableSet(
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
) = MutableObservableSet(this, elementHandler)
|
||||
|
||||
|
||||
fun <T> observableSetOf(
|
||||
vararg elements: T,
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): ObservableSet<T> = ObservableSet(elements.toSet(), elementHandler)
|
||||
|
||||
|
||||
fun <T> mutableObservableSetOf(
|
||||
vararg elements: T,
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): MutableObservableSet<T> = MutableObservableSet(elements.toSet(), elementHandler)
|
||||
|
||||
|
||||
fun <T> observableSetOf(
|
||||
elements: Collection<T> = setOf(),
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): ObservableSet<T> = ObservableSet(elements, elementHandler)
|
||||
|
||||
|
||||
fun <T> mutableObservableSetOf(
|
||||
elements: Collection<T> = setOf(),
|
||||
elementHandler: (T) -> Set<Observable<*>> = { it.getElementObservables() },
|
||||
): MutableObservableSet<T> = MutableObservableSet(elements, elementHandler)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
|
||||
/**
|
||||
* Immutable observable collections implement an [anyChange] observable subscription directly.
|
||||
*
|
||||
* Mutable observable collections implement [anyChange], [addition], & [removal] subscriptions
|
||||
* through a [CollectionSubscription].
|
||||
*
|
||||
* @property anyChange Emits with any mutation of the collection or the collection's observable elements.
|
||||
*
|
||||
* @property addition Emits when any collection mutation results from the addition of an element.
|
||||
*
|
||||
* @property removal Emits when any collection mutation results from the removal of an element.
|
||||
*
|
||||
* @see ListSubscription.set
|
||||
* @see ListSubscription.swap
|
||||
*/
|
||||
interface CollectionSubscription<T, C : Collection<T>> {
|
||||
|
||||
val anyChange: ObservableSubscription<Pair<C, T>>
|
||||
val addition: ObservableSubscription<Pair<C, T>>?
|
||||
val removal: ObservableSubscription<Pair<C, T>>?
|
||||
|
||||
fun unsubscribe()
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
|
||||
data class IndexedListSubscription<T>(
|
||||
val addition: ObservableSubscription<Triple<List<T>, T, Int>>? = null,
|
||||
val removal: ObservableSubscription<Triple<List<T>, T, Int>>? = null,
|
||||
val set: ObservableSubscription<Triple<List<T>, Pair<T, T>, Int>>? = null,
|
||||
val swap: ObservableSubscription<Triple<List<T>, Pair<T, T>, Pair<Int, Int>>>? = null,
|
||||
) {
|
||||
|
||||
fun unsubscribe() {
|
||||
addition?.unsubscribe()
|
||||
removal?.unsubscribe()
|
||||
set?.unsubscribe()
|
||||
swap?.unsubscribe()
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
|
||||
data class ListSubscription<T>(
|
||||
override val anyChange: ObservableSubscription<Pair<List<T>, T>>,
|
||||
override val addition: ObservableSubscription<Pair<List<T>, T>>? = null,
|
||||
override val removal: ObservableSubscription<Pair<List<T>, T>>? = null,
|
||||
val set: ObservableSubscription<Pair<List<T>, Pair<T, T>>>? = null,
|
||||
val swap: ObservableSubscription<Pair<List<T>, Pair<T, T>>>? = null,
|
||||
) : CollectionSubscription<T, List<T>> {
|
||||
|
||||
override fun unsubscribe() {
|
||||
this.anyChange.unsubscribe()
|
||||
addition?.unsubscribe()
|
||||
removal?.unsubscribe()
|
||||
set?.unsubscribe()
|
||||
swap?.unsubscribe()
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.ObservableMap
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.MutableObservableMap
|
||||
|
||||
/**
|
||||
* The immutable [ObservableMap] implements an [anyChange] observable subscription directly.
|
||||
*
|
||||
* The [MutableObservableMap] implements [anyChange], [addition], & [removal] subscriptions
|
||||
* through this class.
|
||||
*
|
||||
* @property anyChange Emits with any mutation from removal, addition, or a mutation in place by
|
||||
* an entry extending the observable interface.
|
||||
*
|
||||
* @property addition Emits when any mutation results from the addition of an element.
|
||||
*
|
||||
* @property removal Emits when any mutation results from the removal of an element.
|
||||
*/
|
||||
data class MapSubscription<K, T>(
|
||||
val anyChange: ObservableSubscription<Pair<Map<K, T>, Pair<K, T>>>,
|
||||
val addition: ObservableSubscription<Pair<Map<K, T>, Pair<K, T>>>? = null,
|
||||
val removal: ObservableSubscription<Pair<Map<K, T>, Pair<K, T>>>? = null,
|
||||
) {
|
||||
|
||||
fun unsubscribe() {
|
||||
anyChange.unsubscribe()
|
||||
addition?.unsubscribe()
|
||||
removal?.unsubscribe()
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.cobblemon.mod.common.api.reactive.collections.subscriptions
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
|
||||
data class SetSubscription<T>(
|
||||
override val anyChange: ObservableSubscription<Pair<Set<T>, T>>,
|
||||
override val addition: ObservableSubscription<Pair<Set<T>, T>>? = null,
|
||||
override val removal: ObservableSubscription<Pair<Set<T>, T>>? = null,
|
||||
) : CollectionSubscription<T, Set<T>> {
|
||||
|
||||
override fun unsubscribe() {
|
||||
this.anyChange.unsubscribe()
|
||||
addition?.unsubscribe()
|
||||
removal?.unsubscribe()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
object AllCollectionTests {
|
||||
|
||||
@Test
|
||||
fun runConstructionTests() {
|
||||
ConstructionTests.setConstruction()
|
||||
ConstructionTests.listConstruction()
|
||||
ConstructionTests.subListConstruction()
|
||||
ConstructionTests.mapConstruction()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runSerializationTests() {
|
||||
SerializationTests.setSerialization()
|
||||
SerializationTests.listSerialization()
|
||||
SerializationTests.subListSerialization()
|
||||
SerializationTests.mapSerialization()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runIteratorTests() {
|
||||
IteratorTests.setIterator()
|
||||
IteratorTests.listIterator()
|
||||
IteratorTests.subListIterator()
|
||||
IteratorTests.mapIterator()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runEmittingTests() {
|
||||
EmittingTests.setAsCollectionEmitChange()
|
||||
EmittingTests.listAsCollectionEmitChange()
|
||||
EmittingTests.listAndSubListEmitChange()
|
||||
EmittingTests.mapEmitChange()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runEmittingReferenceTypesTests() {
|
||||
EmittingReferenceTypeTests.setElementsEmittingChange()
|
||||
EmittingReferenceTypeTests.listElementsEmittingChange()
|
||||
EmittingReferenceTypeTests.subListElementsEmittingChange()
|
||||
EmittingReferenceTypeTests.entriesEmittingChange()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.*
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.*
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.*
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
object ConstructionTests {
|
||||
|
||||
@Test
|
||||
fun setConstruction() {
|
||||
val sets = TestSets.default()
|
||||
|
||||
val inputSet = sets.input
|
||||
val observableSet = sets.observable
|
||||
val mutableObservableSet = sets.mutableObservable
|
||||
val emptyObservableSet = sets.emptyObservable
|
||||
val emptyMutableObservableSet = sets.emptyMutableObservable
|
||||
|
||||
Assertions.assertTrue(observableSet.containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSet.containsAll(inputSet))
|
||||
Assertions.assertTrue(emptyObservableSet.isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservableSet.isEmpty())
|
||||
|
||||
Assertions.assertTrue(ObservableSet(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(ObservableSet(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(ObservableSet(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(ObservableSet(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(ObservableSet(emptyMutableObservableSet).isEmpty())
|
||||
Assertions.assertTrue(ObservableSet<String>(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(ObservableSet<String>(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(ObservableSet<String>(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(ObservableSet<String>(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(ObservableSet<String>(emptyMutableObservableSet).isEmpty())
|
||||
|
||||
Assertions.assertTrue(MutableObservableSet(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(MutableObservableSet(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(MutableObservableSet(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(MutableObservableSet(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableSet(emptyMutableObservableSet).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableSet<String>(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(MutableObservableSet<String>(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(MutableObservableSet<String>(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(MutableObservableSet<String>(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableSet<String>(emptyMutableObservableSet).isEmpty())
|
||||
|
||||
Assertions.assertTrue(inputSet.toObservableSet().containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSet.toObservableSet().containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSet.toObservableSet().containsAll(inputSet))
|
||||
Assertions.assertTrue(emptyObservableSet.toObservableSet().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservableSet.toObservableSet().isEmpty())
|
||||
Assertions.assertTrue(inputSet.toObservableSet<String>().containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSet.toObservableSet<String>().containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSet.toObservableSet<String>().containsAll(inputSet))
|
||||
Assertions.assertTrue(emptyObservableSet.toObservableSet<String>().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservableSet.toObservableSet<String>().isEmpty())
|
||||
|
||||
Assertions.assertTrue(inputSet.toMutableObservableSet().containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSet.toMutableObservableSet().containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSet.toMutableObservableSet().containsAll(inputSet))
|
||||
Assertions.assertTrue(emptyObservableSet.toMutableObservableSet().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservableSet.toMutableObservableSet().isEmpty())
|
||||
Assertions.assertTrue(inputSet.toMutableObservableSet<String>().containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSet.toMutableObservableSet<String>().containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSet.toMutableObservableSet<String>().containsAll(inputSet))
|
||||
Assertions.assertTrue(emptyObservableSet.toMutableObservableSet<String>().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservableSet.toMutableObservableSet<String>().isEmpty())
|
||||
|
||||
Assertions.assertTrue(observableSetOf(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(observableSetOf(emptyMutableObservableSet).isEmpty())
|
||||
Assertions.assertTrue(observableSetOf<String>(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf<String>(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf<String>(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf<String>(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(observableSetOf<String>(emptyMutableObservableSet).isEmpty())
|
||||
|
||||
Assertions.assertTrue(mutableObservableSetOf(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableSetOf(emptyMutableObservableSet).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(inputSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(observableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(mutableObservableSet).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(emptyObservableSet).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(emptyMutableObservableSet).isEmpty())
|
||||
|
||||
val ordered = inputSet.toList()
|
||||
Assertions.assertTrue(observableSetOf(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf<String>(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf<String>(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(observableSetOf<String>(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
|
||||
Assertions.assertTrue(mutableObservableSetOf(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
Assertions.assertTrue(mutableObservableSetOf<String>(ordered[0], ordered[1], ordered[2], ordered[3]).containsAll(inputSet))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listConstruction() {
|
||||
val lists = TestLists.default()
|
||||
val input = lists.input
|
||||
val observable = lists.observable
|
||||
val mutableObservable = lists.mutableObservable
|
||||
val emptyObservable = lists.emptyObservable
|
||||
val emptyMutableObservable = lists.emptyMutableObservable
|
||||
|
||||
Assertions.assertTrue(observable.containsAll(input))
|
||||
Assertions.assertTrue(mutableObservable.containsAll(input))
|
||||
Assertions.assertTrue(emptyObservable.isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservable.isEmpty())
|
||||
|
||||
Assertions.assertTrue(ObservableList(input).containsAll(input))
|
||||
Assertions.assertTrue(ObservableList(observable).containsAll(input))
|
||||
Assertions.assertTrue(ObservableList(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(ObservableList(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(ObservableList(emptyMutableObservable).isEmpty())
|
||||
Assertions.assertTrue(ObservableList<String>(input).containsAll(input))
|
||||
Assertions.assertTrue(ObservableList<String>(observable).containsAll(input))
|
||||
Assertions.assertTrue(ObservableList<String>(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(ObservableList<String>(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(ObservableList<String>(emptyMutableObservable).isEmpty())
|
||||
|
||||
Assertions.assertTrue(MutableObservableList(input).containsAll(input))
|
||||
Assertions.assertTrue(MutableObservableList(observable).containsAll(input))
|
||||
Assertions.assertTrue(MutableObservableList(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(MutableObservableList(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableList(emptyMutableObservable).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableList<String>(input).containsAll(input))
|
||||
Assertions.assertTrue(MutableObservableList<String>(observable).containsAll(input))
|
||||
Assertions.assertTrue(MutableObservableList<String>(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(MutableObservableList<String>(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableList<String>(emptyMutableObservable).isEmpty())
|
||||
|
||||
Assertions.assertTrue(input.toObservableList().containsAll(input))
|
||||
Assertions.assertTrue(observable.toObservableList().containsAll(input))
|
||||
Assertions.assertTrue(mutableObservable.toObservableList().containsAll(input))
|
||||
Assertions.assertTrue(emptyObservable.toObservableList().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservable.toObservableList().isEmpty())
|
||||
Assertions.assertTrue(input.toObservableList<String>().containsAll(input))
|
||||
Assertions.assertTrue(observable.toObservableList<String>().containsAll(input))
|
||||
Assertions.assertTrue(mutableObservable.toObservableList<String>().containsAll(input))
|
||||
Assertions.assertTrue(emptyObservable.toObservableList<String>().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservable.toObservableList<String>().isEmpty())
|
||||
|
||||
Assertions.assertTrue(input.toMutableObservableList().containsAll(input))
|
||||
Assertions.assertTrue(observable.toMutableObservableList().containsAll(input))
|
||||
Assertions.assertTrue(mutableObservable.toMutableObservableList().containsAll(input))
|
||||
Assertions.assertTrue(emptyObservable.toMutableObservableList().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservable.toMutableObservableList().isEmpty())
|
||||
Assertions.assertTrue(input.toMutableObservableList<String>().containsAll(input))
|
||||
Assertions.assertTrue(observable.toMutableObservableList<String>().containsAll(input))
|
||||
Assertions.assertTrue(mutableObservable.toMutableObservableList<String>().containsAll(input))
|
||||
Assertions.assertTrue(emptyObservable.toMutableObservableList<String>().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableObservable.toMutableObservableList<String>().isEmpty())
|
||||
|
||||
Assertions.assertTrue(observableListOf(input).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf(observable).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(observableListOf(emptyMutableObservable).isEmpty())
|
||||
Assertions.assertTrue(observableListOf<String>(input).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf<String>(observable).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf<String>(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf<String>(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(observableListOf<String>(emptyMutableObservable).isEmpty())
|
||||
|
||||
Assertions.assertTrue(mutableObservableListOf(input).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf(observable).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableListOf(emptyMutableObservable).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(input).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(observable).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(mutableObservable).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(emptyObservable).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(emptyMutableObservable).isEmpty())
|
||||
|
||||
Assertions.assertTrue(observableListOf(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf<String>(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf<String>(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(observableListOf<String>(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
|
||||
Assertions.assertTrue(mutableObservableListOf(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(input[0], input[1], input[2], input[3]).containsAll(input))
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subListConstruction() {
|
||||
val lists = TestLists.default()
|
||||
val inputSubList = lists.input.subList(1, 3)
|
||||
val inputSubSubList = inputSubList.subList(0, 1)
|
||||
val subList = lists.observable.subList(1, 3)
|
||||
val subSubList = subList.subList(0, 1)
|
||||
val mutableObservableSubList = lists.mutableObservable.subList(1, 3)
|
||||
val mutableObservableSubSubList = mutableObservableSubList.subList(0, 1)
|
||||
|
||||
Assertions.assertTrue(subList.containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableSubList.containsAll(inputSubList))
|
||||
|
||||
Assertions.assertTrue(ObservableList(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(ObservableList(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(ObservableList(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(ObservableList(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(ObservableList(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(ObservableList(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(ObservableList<String>(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(ObservableList<String>(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(ObservableList<String>(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(ObservableList<String>(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(ObservableList<String>(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(ObservableList<String>(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(MutableObservableList(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(MutableObservableList(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(MutableObservableList(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(MutableObservableList(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(MutableObservableList(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(MutableObservableList(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(MutableObservableList<String>(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(MutableObservableList<String>(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(MutableObservableList<String>(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(MutableObservableList<String>(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(MutableObservableList<String>(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(MutableObservableList<String>(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(observableListOf(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(observableListOf(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(observableListOf(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(observableListOf(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(observableListOf(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(observableListOf(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(observableListOf<String>(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(observableListOf<String>(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(observableListOf<String>(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(observableListOf<String>(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(observableListOf<String>(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(observableListOf<String>(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(mutableObservableListOf(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(inputSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(subList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(mutableObservableSubList).containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(inputSubSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(subSubList).containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableListOf<String>(mutableObservableSubSubList).containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(inputSubList.toObservableList().containsAll(inputSubList))
|
||||
Assertions.assertTrue(subList.toObservableList().containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableSubList.toObservableList().containsAll(inputSubList))
|
||||
Assertions.assertTrue(inputSubSubList.toObservableList().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(subSubList.toObservableList().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableSubSubList.toObservableList().containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(inputSubList.toObservableList<String>().containsAll(inputSubList))
|
||||
Assertions.assertTrue(subList.toObservableList<String>().containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableSubList.toObservableList<String>().containsAll(inputSubList))
|
||||
Assertions.assertTrue(inputSubSubList.toObservableList<String>().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(subSubList.toObservableList<String>().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableSubSubList.toObservableList<String>().containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(inputSubList.toMutableObservableList().containsAll(inputSubList))
|
||||
Assertions.assertTrue(subList.toMutableObservableList().containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableSubList.toMutableObservableList().containsAll(inputSubList))
|
||||
Assertions.assertTrue(inputSubSubList.toMutableObservableList().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(subSubList.toMutableObservableList().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableSubSubList.toMutableObservableList().containsAll(inputSubSubList))
|
||||
|
||||
Assertions.assertTrue(inputSubList.toMutableObservableList<String>().containsAll(inputSubList))
|
||||
Assertions.assertTrue(subList.toMutableObservableList<String>().containsAll(inputSubList))
|
||||
Assertions.assertTrue(mutableObservableSubList.toMutableObservableList<String>().containsAll(inputSubList))
|
||||
Assertions.assertTrue(inputSubSubList.toMutableObservableList<String>().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(subSubList.toMutableObservableList<String>().containsAll(inputSubSubList))
|
||||
Assertions.assertTrue(mutableObservableSubSubList.toMutableObservableList<String>().containsAll(inputSubSubList))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mapConstruction() {
|
||||
val testMaps = TestMaps.default()
|
||||
|
||||
val inputMap = testMaps.input
|
||||
val map = testMaps.observable
|
||||
val mutableMap = testMaps.mutableObservable
|
||||
val emptyMap = testMaps.emptyObservable
|
||||
val emptyMutableMap = testMaps.emptyMutableObservable
|
||||
val entries = inputMap.entries.toList()
|
||||
val pairs = entries.pairs().toList()
|
||||
|
||||
Assertions.assertTrue(map.containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableMap.containsAll(inputMap))
|
||||
Assertions.assertTrue(emptyMap.isEmpty())
|
||||
Assertions.assertTrue(emptyMutableMap.isEmpty())
|
||||
|
||||
Assertions.assertTrue(ObservableMap(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(ObservableMap(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(ObservableMap(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(ObservableMap(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(ObservableMap(emptyMutableMap).isEmpty())
|
||||
Assertions.assertTrue(ObservableMap<Int, String>(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(ObservableMap<Int, String>(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(ObservableMap<Int, String>(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(ObservableMap<Int, String>(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(ObservableMap<Int, String>(emptyMutableMap).isEmpty())
|
||||
|
||||
Assertions.assertTrue(MutableObservableMap(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(MutableObservableMap(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(MutableObservableMap(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(MutableObservableMap(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableMap(emptyMutableMap).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableMap<Int, String>(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(MutableObservableMap<Int, String>(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(MutableObservableMap<Int, String>(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(MutableObservableMap<Int, String>(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(MutableObservableMap<Int, String>(emptyMutableMap).isEmpty())
|
||||
|
||||
Assertions.assertTrue(inputMap.toObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(map.toObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableMap.toObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(pairs.toObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(emptyMap.toObservableMap().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableMap.toObservableMap().isEmpty())
|
||||
Assertions.assertTrue(inputMap.toObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(map.toObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableMap.toObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(pairs.toObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(emptyMap.toObservableMap<Int, String>().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableMap.toObservableMap<Int, String>().isEmpty())
|
||||
|
||||
Assertions.assertTrue(inputMap.toMutableObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(map.toMutableObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableMap.toMutableObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(pairs.toMutableObservableMap().containsAll(inputMap))
|
||||
Assertions.assertTrue(emptyMap.toMutableObservableMap().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableMap.toMutableObservableMap().isEmpty())
|
||||
Assertions.assertTrue(inputMap.toMutableObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(map.toMutableObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableMap.toMutableObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(pairs.toMutableObservableMap<Int, String>().containsAll(inputMap))
|
||||
Assertions.assertTrue(emptyMap.toMutableObservableMap<Int, String>().isEmpty())
|
||||
Assertions.assertTrue(emptyMutableMap.toMutableObservableMap<Int, String>().isEmpty())
|
||||
|
||||
Assertions.assertTrue(observableMapOf(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf(pairs).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(observableMapOf(emptyMutableMap).isEmpty())
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(pairs).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(emptyMutableMap).isEmpty())
|
||||
|
||||
Assertions.assertTrue(mutableObservableMapOf(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf(pairs).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableMapOf(emptyMutableMap).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(inputMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(map).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(mutableMap).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(pairs).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(emptyMap).isEmpty())
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(emptyMutableMap).isEmpty())
|
||||
|
||||
Assertions.assertTrue(observableMapOf(entries[0], entries[1], entries[2], entries[3]).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf(pairs[0], pairs[1], pairs[2], pairs[3]).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(entries[0], entries[1], entries[2], entries[3]).containsAll(inputMap))
|
||||
Assertions.assertTrue(observableMapOf<Int, String>(pairs[0], pairs[1], pairs[2], pairs[3]).containsAll(inputMap))
|
||||
|
||||
Assertions.assertTrue(mutableObservableMapOf(entries[0], entries[1], entries[2], entries[3]).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf(pairs[0], pairs[1], pairs[2], pairs[3]).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(entries[0], entries[1], entries[2], entries[3]).containsAll(inputMap))
|
||||
Assertions.assertTrue(mutableObservableMapOf<Int, String>(pairs[0], pairs[1], pairs[2], pairs[3]).containsAll(inputMap))
|
||||
}
|
||||
}
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.MutableObservableList
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.MutableObservableMap
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.MutableObservableSet
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
object EmittingReferenceTypeTests {
|
||||
|
||||
@Test
|
||||
fun setElementsEmittingChange() = elementsEmittingChange(MutableObservableSet())
|
||||
|
||||
@Test
|
||||
fun listElementsEmittingChange() = elementsEmittingChange(MutableObservableList())
|
||||
|
||||
fun <C : Collection<SettableObservable<Int>>> elementsEmittingChange(
|
||||
mutableObservableCollection: MutableObservableCollection<SettableObservable<Int>, C>
|
||||
) {
|
||||
val getNew = { SettableObservable(0) }
|
||||
val addNew = { c: MutableCollection<SettableObservable<Int>>, count: Int ->
|
||||
for (i in 0 until count) {
|
||||
c.add(getNew())
|
||||
}
|
||||
}
|
||||
|
||||
addNew(mutableObservableCollection, 4)
|
||||
|
||||
val observableCollection = mutableObservableCollection.copy()
|
||||
|
||||
var anyChangeTally = 0
|
||||
var subscription = observableCollection.subscribe { anyChangeTally++ }
|
||||
|
||||
var mutableAnyChangeTally = 0
|
||||
var mutableSubscription = mutableObservableCollection.subscribe { mutableAnyChangeTally++ }
|
||||
|
||||
val incrementValues = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
mutableObservableCollection.forEach { it.set(it.get() + 1) }
|
||||
}
|
||||
iterations * observableCollection.size
|
||||
}
|
||||
|
||||
var newEmits = incrementValues(1)
|
||||
var expectedTally = newEmits
|
||||
var mutableExpectedTally = newEmits
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
mutableSubscription.unsubscribe()
|
||||
|
||||
newEmits = incrementValues(1)
|
||||
expectedTally += newEmits
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
mutableSubscription = mutableObservableCollection.subscribe { mutableAnyChangeTally++ }
|
||||
|
||||
newEmits = incrementValues(1)
|
||||
expectedTally += newEmits
|
||||
mutableExpectedTally += newEmits
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
addNew(mutableObservableCollection, 4)
|
||||
|
||||
mutableExpectedTally += 4
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
newEmits = incrementValues(1)
|
||||
expectedTally += newEmits
|
||||
mutableExpectedTally += newEmits + (mutableObservableCollection.size - observableCollection.size)
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
mutableExpectedTally += mutableObservableCollection.size
|
||||
mutableObservableCollection.clear()
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
incrementValues(1)
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subListElementsEmittingChange() {
|
||||
val getNew = { SettableObservable(0) }
|
||||
val addNew = { list: MutableObservableList<SettableObservable<Int>>, count: Int ->
|
||||
for (i in 0 until count) {
|
||||
list.add(getNew())
|
||||
}
|
||||
}
|
||||
|
||||
val mutableObservableList = MutableObservableList<SettableObservable<Int>>()
|
||||
addNew(mutableObservableList, 4)
|
||||
|
||||
val subList = mutableObservableList.subList(1, 3)
|
||||
|
||||
var anyChangeTally = 0
|
||||
var subListAnyChangeTally = 0
|
||||
|
||||
val listSub = mutableObservableList.subscribe { anyChangeTally++ }
|
||||
val subListSub = subList.subscribe { subListAnyChangeTally++ }
|
||||
|
||||
val originalZeroElement = mutableObservableList[0]
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
var expectedTally = 1
|
||||
var expectedSubListTally = 0
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
|
||||
// originalZeroElement pushed into of subList window
|
||||
|
||||
mutableObservableList.add(0, getNew())
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 2
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 1
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
|
||||
|
||||
mutableObservableList.add(0, getNew())
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 2
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 1
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
|
||||
// originalZeroElement pushed out of subList window
|
||||
|
||||
mutableObservableList.add(0, getNew())
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 2
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 0
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
|
||||
// originalZeroElement drops back into subList window
|
||||
|
||||
mutableObservableList.removeAt(0)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 2
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 1
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
|
||||
|
||||
mutableObservableList.removeAt(0)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 2
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 1
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
|
||||
// originalZeroElement drops out of subList window
|
||||
|
||||
mutableObservableList.removeAt(0)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 2
|
||||
|
||||
originalZeroElement.set(originalZeroElement.get() + 1)
|
||||
expectedTally += 1
|
||||
expectedSubListTally += 0
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(subListAnyChangeTally == expectedSubListTally)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun entriesEmittingChange() {
|
||||
val getNew = { SettableObservable(0) }
|
||||
val putNew = {
|
||||
map: MutableObservableMap<SettableObservable<Int>, SettableObservable<Int>>,
|
||||
count: Int,
|
||||
->
|
||||
for (i in 0 until count) {
|
||||
map.put(getNew(), getNew())
|
||||
}
|
||||
}
|
||||
|
||||
val mutableObservableMap = MutableObservableMap<SettableObservable<Int>, SettableObservable<Int>>()
|
||||
|
||||
putNew(mutableObservableMap, 4)
|
||||
|
||||
val observableMap = mutableObservableMap.copy()
|
||||
|
||||
var anyChangeTally = 0
|
||||
var subscription = observableMap.subscribe { anyChangeTally++ }
|
||||
|
||||
var mutableAnyChangeTally = 0
|
||||
var mutableSubscription = mutableObservableMap.subscribe { mutableAnyChangeTally++ }
|
||||
|
||||
val incrementValues = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
mutableObservableMap.forEach { (key, value) ->
|
||||
key.set(key.get() + 1)
|
||||
value.set(value.get() + 1)
|
||||
}
|
||||
}
|
||||
iterations * observableMap.size * 2
|
||||
}
|
||||
|
||||
var newEmits = incrementValues(1)
|
||||
var expectedTally = newEmits
|
||||
var mutableExpectedTally = newEmits
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
mutableSubscription.unsubscribe()
|
||||
|
||||
newEmits = incrementValues(1)
|
||||
expectedTally += newEmits
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
mutableSubscription = mutableObservableMap.subscribe { mutableAnyChangeTally++ }
|
||||
|
||||
newEmits = incrementValues(1)
|
||||
expectedTally += newEmits
|
||||
mutableExpectedTally += newEmits
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
putNew(mutableObservableMap, 4)
|
||||
|
||||
mutableExpectedTally += 4
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
newEmits = incrementValues(1)
|
||||
expectedTally += newEmits
|
||||
mutableExpectedTally += newEmits + (2 * (mutableObservableMap.size - observableMap.size))
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
mutableExpectedTally += mutableObservableMap.size
|
||||
mutableObservableMap.clear()
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
|
||||
incrementValues(1)
|
||||
|
||||
Assertions.assertTrue(anyChangeTally == expectedTally)
|
||||
Assertions.assertTrue(mutableAnyChangeTally == mutableExpectedTally)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,835 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.MutableObservableList
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.mutableObservableMapOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.MutableObservableSet
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
object EmittingTests {
|
||||
|
||||
@Test
|
||||
fun setAsCollectionEmitChange() = collectionEmitChange(MutableObservableSet())
|
||||
|
||||
@Test
|
||||
fun listAsCollectionEmitChange() = collectionEmitChange(MutableObservableList())
|
||||
|
||||
private fun collectionEmitChange(elements: MutableObservableCollection<String, *>) {
|
||||
var anyChangeOne = 0
|
||||
var additionOne = 0
|
||||
var removalOne = 0
|
||||
|
||||
var anyChangeTwo = 0
|
||||
var additionTwo = 0
|
||||
var removalTwo = 0
|
||||
|
||||
var expectedAnyChangeOne = 0
|
||||
var expectedAdditionOne = 0
|
||||
var expectedRemovalOne = 0
|
||||
|
||||
var expectedAnyChangeTwo = 0
|
||||
var expectedAdditionTwo = 0
|
||||
var expectedRemovalTwo = 0
|
||||
|
||||
val confirmExpected = {
|
||||
Assertions.assertTrue(anyChangeOne == expectedAnyChangeOne)
|
||||
Assertions.assertTrue(additionOne == expectedAdditionOne)
|
||||
Assertions.assertTrue(removalOne == expectedRemovalOne)
|
||||
Assertions.assertTrue(anyChangeTwo == expectedAnyChangeTwo)
|
||||
Assertions.assertTrue(additionTwo == expectedAdditionTwo)
|
||||
Assertions.assertTrue(removalTwo == expectedRemovalTwo)
|
||||
}
|
||||
|
||||
val getNewSubOne = {
|
||||
elements.subscribe(
|
||||
anyChangeHandler = { anyChangeOne++ },
|
||||
additionHandler = { additionOne++ },
|
||||
removalHandler = { removalOne++ },
|
||||
)
|
||||
}
|
||||
|
||||
val getNewSubTwo = {
|
||||
elements.subscribe(
|
||||
anyChangeHandler = { anyChangeTwo++ },
|
||||
additionHandler = { additionTwo++ },
|
||||
removalHandler = { removalTwo++ },
|
||||
)
|
||||
}
|
||||
|
||||
val subscriptionOne = getNewSubOne()
|
||||
val subscriptionTwo = getNewSubTwo()
|
||||
|
||||
var subbedAnyChangeOne = true
|
||||
var subbedAdditionOne = true
|
||||
var subbedRemovalOne = true
|
||||
|
||||
var subbedAnyChangeTwo = true
|
||||
var subbedAdditionTwo = true
|
||||
var subbedRemovalTwo = true
|
||||
|
||||
val incrementExpectedAddition = { value: Int ->
|
||||
if (subbedAnyChangeOne) expectedAnyChangeOne += value
|
||||
if (subbedAdditionOne) expectedAdditionOne += value
|
||||
if (subbedAnyChangeTwo) expectedAnyChangeTwo += value
|
||||
if (subbedAdditionTwo) expectedAdditionTwo += value
|
||||
}
|
||||
val incrementExpectedRemoval = { value: Int ->
|
||||
if (subbedAnyChangeOne) expectedAnyChangeOne += value
|
||||
if (subbedRemovalOne) expectedRemovalOne += value
|
||||
if (subbedAnyChangeTwo) expectedAnyChangeTwo += value
|
||||
if (subbedRemovalTwo) expectedRemovalTwo += value
|
||||
}
|
||||
|
||||
var currentCount = 0
|
||||
val next = { "${currentCount++}" }
|
||||
val bulkInput = listOf(next(), next(), next(), next())
|
||||
|
||||
val addNew = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.add(next())) incrementExpectedAddition(1)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val addDuplicates = { iterations: Int ->
|
||||
elements.firstOrNull() ?. let{ dupe ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.add(dupe)) incrementExpectedAddition(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val addNewIfTrue = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.addIf(next()) { true }) incrementExpectedAddition(1)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val addNewIfFalse = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.addIf(next()) { false }) incrementExpectedAddition(1)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val addDupeIfTrue = { iterations: Int ->
|
||||
elements.firstOrNull() ?. let{ dupe ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.addIf(dupe) { true }) incrementExpectedAddition(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val addDupeIfFalse = { iterations: Int ->
|
||||
elements.firstOrNull() ?. let{ dupe ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.addIf(dupe) { false }) incrementExpectedAddition(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val remove = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
elements.firstOrNull()?.let { if (elements.remove(it)) incrementExpectedRemoval(1) }
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val removeNotContained = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.remove("I am not here...")) incrementExpectedRemoval(1)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val removeDupeIfTrue = { iterations: Int ->
|
||||
elements.firstOrNull() ?. let{ dupe ->
|
||||
for (i in 0 until iterations) {
|
||||
val size = elements.size
|
||||
elements.removeIf { it == dupe }
|
||||
val change = size - elements.size
|
||||
if (change > 0) incrementExpectedRemoval(change)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val removeDupeIfFalse = { iterations: Int ->
|
||||
elements.firstOrNull() ?. let{ dupe ->
|
||||
for (i in 0 until iterations) {
|
||||
if (elements.removeIf { false }) incrementExpectedRemoval(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val addAll = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
val size = elements.size
|
||||
if (elements.addAll(bulkInput)) incrementExpectedAddition(elements.size - size)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val removeAll = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
val size = elements.size
|
||||
if (elements.removeAll(bulkInput)) incrementExpectedRemoval(size - elements.size)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val retainAll = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
val size = elements.size
|
||||
if (elements.retainAll(bulkInput)) incrementExpectedRemoval(size - elements.size)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val clear = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
val size = elements.size
|
||||
elements.clear()
|
||||
val change = size - elements.size
|
||||
if (change > 0) incrementExpectedRemoval(change)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val runAll = { iterations: Int ->
|
||||
addNew(iterations)
|
||||
addDuplicates(iterations)
|
||||
addNewIfTrue(iterations)
|
||||
addNewIfFalse(iterations)
|
||||
addDupeIfTrue(iterations)
|
||||
addDupeIfFalse(iterations)
|
||||
remove(iterations)
|
||||
removeNotContained(iterations)
|
||||
removeDupeIfTrue(iterations)
|
||||
removeDupeIfFalse(iterations)
|
||||
addAll(iterations)
|
||||
removeAll(iterations)
|
||||
retainAll(iterations)
|
||||
clear(iterations)
|
||||
}
|
||||
|
||||
runAll(8)
|
||||
|
||||
subscriptionOne.unsubscribe()
|
||||
subbedAnyChangeOne = false
|
||||
subbedAdditionOne = false
|
||||
subbedRemovalOne = false
|
||||
runAll(8)
|
||||
|
||||
subscriptionTwo.anyChange.unsubscribe()
|
||||
subbedAnyChangeTwo = false
|
||||
runAll(8)
|
||||
|
||||
subscriptionTwo.addition?.unsubscribe()
|
||||
subscriptionTwo.removal?.unsubscribe()
|
||||
subbedAdditionTwo = false
|
||||
subbedRemovalTwo = false
|
||||
runAll(8)
|
||||
|
||||
val newSubOne = getNewSubOne()
|
||||
newSubOne.addition?.unsubscribe()
|
||||
newSubOne.removal?.unsubscribe()
|
||||
subbedAnyChangeOne = true
|
||||
runAll(8)
|
||||
|
||||
val newSubTwo = getNewSubTwo()
|
||||
newSubTwo.addition?.unsubscribe()
|
||||
newSubTwo.removal?.unsubscribe()
|
||||
subbedAnyChangeTwo = true
|
||||
runAll(8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listAndSubListEmitChange() {
|
||||
var anyChangeList = 0
|
||||
var additionList = 0
|
||||
var removalList = 0
|
||||
var setList = 0
|
||||
var swapList = 0
|
||||
var anyChangeSubList = 0
|
||||
var additionSubList = 0
|
||||
var removalSubList = 0
|
||||
var setSubList = 0
|
||||
var swapSubList = 0
|
||||
|
||||
var expectedAnyChangeList = 0
|
||||
var expectedAdditionList = 0
|
||||
var expectedRemovalList = 0
|
||||
var expectedSetList = 0
|
||||
var expectedSwapList = 0
|
||||
var expectedAnyChangeSubList = 0
|
||||
var expectedAdditionSubList = 0
|
||||
var expectedRemovalSubList = 0
|
||||
var expectedSetSubList = 0
|
||||
var expectedSwapSubList = 0
|
||||
|
||||
val input = listOf("zero", "one", "two", "three")
|
||||
val list = MutableObservableList(input)
|
||||
val fromIndex = 1
|
||||
val toIndex = 3
|
||||
val subList = list.subList(fromIndex, toIndex)
|
||||
val isInWindow: Int.() -> Boolean = { this in fromIndex until toIndex }
|
||||
|
||||
val listSub = list.subscribe(
|
||||
priority = Priority.NORMAL,
|
||||
anyChangeHandler = { anyChangeList++ },
|
||||
additionHandler = { additionList++ },
|
||||
removalHandler = { removalList++ },
|
||||
setHandler = { setList++ },
|
||||
swapHandler = { swapList++ },
|
||||
)
|
||||
val subListSub = subList.subscribe(
|
||||
priority = Priority.NORMAL,
|
||||
anyChangeHandler = { anyChangeSubList++ },
|
||||
additionHandler = { additionSubList++ },
|
||||
removalHandler = { removalSubList++ },
|
||||
setHandler = { setSubList++ },
|
||||
swapHandler = { swapSubList++ },
|
||||
)
|
||||
|
||||
val subscribedToAnyChange = true
|
||||
val subscribedToAddition = true
|
||||
val subscribedToRemoval = true
|
||||
val subscribedToSet = true
|
||||
val subscribedToSwap = true
|
||||
val subscribedToSubListAnyChange = true
|
||||
val subscribedToSubListAddition = true
|
||||
val subscribedToSubListRemoval = true
|
||||
val subscribedToSubListSet = true
|
||||
val subscribedToSubListSwap = true
|
||||
|
||||
val incrementAddition = { count: Int, index: Int ->
|
||||
if (subscribedToAnyChange) expectedAnyChangeList += count
|
||||
if (subscribedToAddition) expectedAdditionList += count
|
||||
if (index < toIndex && fromIndex <= list.lastIndex) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count
|
||||
if (subscribedToSubListAddition) expectedAdditionSubList += count
|
||||
// removal for the elements pushed out of the sublist window
|
||||
if (toIndex <= list.lastIndex) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count
|
||||
if (subscribedToSubListRemoval) expectedRemovalSubList += count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val incrementRemoval = { count: Int, index: Int ->
|
||||
if (subscribedToAnyChange) expectedAnyChangeList += count
|
||||
if (subscribedToRemoval) expectedRemovalList += count
|
||||
if (index < toIndex) {
|
||||
if (fromIndex <= list.size) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count
|
||||
if (subscribedToSubListRemoval) expectedRemovalSubList += count
|
||||
}
|
||||
// addition for the elements dropping into the sublist window
|
||||
if (toIndex <= list.size) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count
|
||||
if (subscribedToSubListAddition) expectedAdditionSubList += count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val incrementSet = { count: Int, index: Int ->
|
||||
if (subscribedToAnyChange) expectedAnyChangeList += count * 2
|
||||
if (subscribedToSet) expectedSetList += count
|
||||
if (index.isInWindow()) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count * 2
|
||||
if (subscribedToSubListSet) expectedSetSubList += count
|
||||
}
|
||||
}
|
||||
|
||||
val incrementSwap = { count: Int, indexOne: Int, indexTwo: Int ->
|
||||
if (subscribedToAnyChange) expectedAnyChangeList += count * 2
|
||||
if (subscribedToSwap) expectedSwapList += count
|
||||
if (indexOne.isInWindow() && indexTwo.isInWindow()) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count * 2
|
||||
if (subscribedToSubListSwap) expectedSwapSubList += count
|
||||
} else if ((!indexOne.isInWindow() && indexTwo.isInWindow()) ||
|
||||
(indexOne.isInWindow() && !indexTwo.isInWindow())) {
|
||||
if (subscribedToSubListAnyChange) expectedAnyChangeSubList += count * 2
|
||||
if (subscribedToSubListRemoval) expectedAdditionSubList += count
|
||||
if (subscribedToAnyChange) expectedRemovalSubList += count
|
||||
}
|
||||
}
|
||||
|
||||
val confirmExpected = {
|
||||
Assertions.assertTrue(anyChangeList == expectedAnyChangeList)
|
||||
Assertions.assertTrue(additionList == expectedAdditionList)
|
||||
Assertions.assertTrue(removalList == expectedRemovalList)
|
||||
Assertions.assertTrue(setList == expectedSetList)
|
||||
Assertions.assertTrue(swapList == expectedSwapList)
|
||||
Assertions.assertTrue(anyChangeSubList == expectedAnyChangeSubList)
|
||||
Assertions.assertTrue(additionSubList == expectedAdditionSubList)
|
||||
Assertions.assertTrue(removalSubList == expectedRemovalSubList)
|
||||
Assertions.assertTrue(setSubList == expectedSetSubList)
|
||||
Assertions.assertTrue(swapSubList == expectedSwapSubList)
|
||||
}
|
||||
|
||||
val confirmSynced: () -> Unit = {
|
||||
for (i in 0 until subList.size) {
|
||||
if (list.size > i + fromIndex) {
|
||||
Assertions.assertTrue(subList[i] == list[i + fromIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentCount = 0
|
||||
val next = { "${currentCount++}" }
|
||||
|
||||
val add = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
list.add(next())
|
||||
incrementAddition(1, list.lastIndex)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val addIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
list.addIf(next()) { bool }
|
||||
if (bool) {
|
||||
incrementAddition(1, list.lastIndex)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val addAtStartIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.addAtIf(0, next()) { bool }
|
||||
if (bool) {
|
||||
incrementAddition(1, 0)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val addAtEndIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.addAtIf(list.lastIndex, next()) { bool }
|
||||
if (bool) {
|
||||
incrementAddition(1, list.lastIndex - 1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val removeFromStart = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.firstOrNull()?.let {
|
||||
if (list.remove(it)) {
|
||||
incrementRemoval(1, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val removeFromEnd = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.removeLast()
|
||||
incrementRemoval(1, list.size)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val removeAtStartIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.removeAtIf(0) { bool }
|
||||
if (bool) {
|
||||
incrementRemoval(1, 0)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val removeAtEndIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.removeAtIf(list.lastIndex) { bool }
|
||||
if (bool) {
|
||||
incrementRemoval(1, list.lastIndex)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val setStart = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list[0] = next()
|
||||
incrementSet(1, 0)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val setEnd = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list[list.lastIndex] = next()
|
||||
incrementSet(1, list.lastIndex)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val setStartIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.setIf(0, next()) { bool }
|
||||
if (bool) {
|
||||
incrementSet(1, 0)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val setEndIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list.setIf(list.lastIndex, next()) { bool }
|
||||
if (bool) {
|
||||
incrementSet(1, list.lastIndex)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val swapStart = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list[0] = next()
|
||||
incrementSet(1, 0)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val swapEnd = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.isEmpty()) {
|
||||
add(1)
|
||||
}
|
||||
list[list.lastIndex] = next()
|
||||
incrementSet(1, list.lastIndex)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val swapStartIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.size < 2) {
|
||||
add(2)
|
||||
}
|
||||
list.swapIf(0, 1) { bool }
|
||||
if (bool) {
|
||||
incrementSwap(1, 0, 1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val swapEndIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.size < 2) {
|
||||
add(2)
|
||||
}
|
||||
list.swapIf(list.lastIndex, list.lastIndex - 1) { bool }
|
||||
if (bool) {
|
||||
incrementSwap(1, list.lastIndex, list.lastIndex - 1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val swapInsideOut = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.size < fromIndex) {
|
||||
add(fromIndex + 1)
|
||||
}
|
||||
list.swap(0, fromIndex)
|
||||
incrementSwap(1, 0, fromIndex)
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val swapInsideOutIf = { iterations: Int, bool: Boolean ->
|
||||
for (i in 0 until iterations) {
|
||||
if (list.size < fromIndex) {
|
||||
add(fromIndex + 1)
|
||||
}
|
||||
list.swapIf(0, fromIndex) { bool }
|
||||
if (bool) {
|
||||
incrementSwap(1, 0, fromIndex)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
confirmSynced()
|
||||
}
|
||||
|
||||
val runAll = { startingSize: Int ->
|
||||
if (list.size < startingSize) {
|
||||
add(startingSize - list.size)
|
||||
} else if (list.size > startingSize) {
|
||||
removeFromStart(list.size - startingSize)
|
||||
}
|
||||
add(1)
|
||||
addIf(1, true)
|
||||
addIf(1, false)
|
||||
addAtStartIf(1, true)
|
||||
addAtStartIf(1, false)
|
||||
addAtEndIf(1, true)
|
||||
addAtEndIf(1, false)
|
||||
removeFromStart(1)
|
||||
removeFromEnd(1)
|
||||
removeAtStartIf(1, true)
|
||||
removeAtEndIf(1, false)
|
||||
setStart(1)
|
||||
setStartIf(1, true)
|
||||
setStartIf(1, false)
|
||||
setEnd(1)
|
||||
setEndIf(1, true)
|
||||
setEndIf(1, false)
|
||||
swapStart(1)
|
||||
swapStartIf(1, true)
|
||||
swapStartIf(1, false)
|
||||
swapEnd(1)
|
||||
swapEndIf(1, true)
|
||||
swapEndIf(1, false)
|
||||
swapInsideOut(1)
|
||||
swapInsideOutIf(1, true)
|
||||
swapInsideOutIf(1, false)
|
||||
}
|
||||
|
||||
runAll(0)
|
||||
runAll(2)
|
||||
runAll(10)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mapEmitChange() {
|
||||
val mutableMap = mutableObservableMapOf(TestMaps.defaultMap())
|
||||
var anyChangeOne = 0
|
||||
var additionOne = 0
|
||||
var removalOne = 0
|
||||
|
||||
var anyChangeTwo = 0
|
||||
var additionTwo = 0
|
||||
var removalTwo = 0
|
||||
|
||||
var expectedAnyChangeOne = 0
|
||||
var expectedAdditionOne = 0
|
||||
var expectedRemovalOne = 0
|
||||
|
||||
var expectedAnyChangeTwo = 0
|
||||
var expectedAdditionTwo = 0
|
||||
var expectedRemovalTwo = 0
|
||||
|
||||
val confirmExpected = {
|
||||
Assertions.assertTrue(anyChangeOne == expectedAnyChangeOne)
|
||||
Assertions.assertTrue(additionOne == expectedAdditionOne)
|
||||
Assertions.assertTrue(removalOne == expectedRemovalOne)
|
||||
Assertions.assertTrue(anyChangeTwo == expectedAnyChangeTwo)
|
||||
Assertions.assertTrue(additionTwo == expectedAdditionTwo)
|
||||
Assertions.assertTrue(removalTwo == expectedRemovalTwo)
|
||||
}
|
||||
|
||||
val getNewSubOne = {
|
||||
mutableMap.subscribe(
|
||||
anyChangeHandler = { anyChangeOne++ },
|
||||
additionHandler = { additionOne++ },
|
||||
removalHandler = { removalOne++ },
|
||||
)
|
||||
}
|
||||
|
||||
val getNewSubTwo = {
|
||||
mutableMap.subscribe(
|
||||
anyChangeHandler = { anyChangeTwo++ },
|
||||
additionHandler = { additionTwo++ },
|
||||
removalHandler = { removalTwo++ },
|
||||
)
|
||||
}
|
||||
|
||||
val subscriptionOne = getNewSubOne()
|
||||
val subscriptionTwo = getNewSubTwo()
|
||||
|
||||
var subbedAnyChangeOne = true
|
||||
var subbedAdditionOne = true
|
||||
var subbedRemovalOne = true
|
||||
|
||||
var subbedAnyChangeTwo = true
|
||||
var subbedAdditionTwo = true
|
||||
var subbedRemovalTwo = true
|
||||
|
||||
val incrementExpectedAddition = { value: Int ->
|
||||
if (subbedAnyChangeOne) expectedAnyChangeOne += value
|
||||
if (subbedAdditionOne) expectedAdditionOne += value
|
||||
if (subbedAnyChangeTwo) expectedAnyChangeTwo += value
|
||||
if (subbedAdditionTwo) expectedAdditionTwo += value
|
||||
}
|
||||
val incrementExpectedRemoval = { value: Int ->
|
||||
if (subbedAnyChangeOne) expectedAnyChangeOne += value
|
||||
if (subbedRemovalOne) expectedRemovalOne += value
|
||||
if (subbedAnyChangeTwo) expectedAnyChangeTwo += value
|
||||
if (subbedRemovalTwo) expectedRemovalTwo += value
|
||||
}
|
||||
|
||||
var currentCount = 0
|
||||
val next = { currentCount to "${currentCount++}" }
|
||||
val bulkInput = mapOf(next(), next(), next(), next())
|
||||
|
||||
val putNew = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (mutableMap.put(next()) != null) {
|
||||
incrementExpectedRemoval(1)
|
||||
}
|
||||
incrementExpectedAddition(1)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val putCurrentKeys = { iterations: Int ->
|
||||
mutableMap.firstKeyOrNull()?. let { dupe ->
|
||||
for (i in 0 until iterations) {
|
||||
if (mutableMap.put(dupe, next().second) != null) {
|
||||
incrementExpectedRemoval(1)
|
||||
}
|
||||
incrementExpectedAddition(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val removeNew = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (mutableMap.remove(next().first) != null) {
|
||||
incrementExpectedRemoval(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val removeCurrent = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
if (mutableMap.remove(next().first) != null) {
|
||||
incrementExpectedRemoval(1)
|
||||
}
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val putAll = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
var replacedCount = 0
|
||||
bulkInput.forEach { if (mutableMap.keys.contains(it.key)) replacedCount++ }
|
||||
mutableMap.putAll(bulkInput)
|
||||
incrementExpectedAddition(bulkInput.size)
|
||||
incrementExpectedRemoval(replacedCount)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val clear = { iterations: Int ->
|
||||
for (i in 0 until iterations) {
|
||||
val size = mutableMap.size
|
||||
mutableMap.clear()
|
||||
val change = size - mutableMap.size
|
||||
if (change > 0) incrementExpectedRemoval(change)
|
||||
}
|
||||
confirmExpected()
|
||||
}
|
||||
|
||||
val runAll = { iterations: Int ->
|
||||
putNew(iterations)
|
||||
putCurrentKeys(iterations)
|
||||
removeNew(iterations)
|
||||
removeCurrent(iterations)
|
||||
putAll(iterations)
|
||||
clear(iterations)
|
||||
}
|
||||
|
||||
runAll(8)
|
||||
|
||||
subscriptionOne.unsubscribe()
|
||||
subbedAnyChangeOne = false
|
||||
subbedAdditionOne = false
|
||||
subbedRemovalOne = false
|
||||
runAll(8)
|
||||
|
||||
subscriptionTwo.anyChange.unsubscribe()
|
||||
subbedAnyChangeTwo = false
|
||||
runAll(8)
|
||||
|
||||
subscriptionTwo.addition?.unsubscribe()
|
||||
subscriptionTwo.removal?.unsubscribe()
|
||||
subbedAdditionTwo = false
|
||||
subbedRemovalTwo = false
|
||||
runAll(8)
|
||||
|
||||
val newSubOne = getNewSubOne()
|
||||
newSubOne.addition?.unsubscribe()
|
||||
newSubOne.removal?.unsubscribe()
|
||||
subbedAnyChangeOne = true
|
||||
runAll(8)
|
||||
|
||||
val newSubTwo = getNewSubTwo()
|
||||
newSubTwo.addition?.unsubscribe()
|
||||
newSubTwo.removal?.unsubscribe()
|
||||
subbedAnyChangeTwo = true
|
||||
runAll(8)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.removeIf
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
object IteratorTests {
|
||||
|
||||
@Test
|
||||
fun setIterator() {
|
||||
val testSets = TestSets.default()
|
||||
iteratorTest(testSets)
|
||||
bulkMutationTest(testSets)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun listIterator() {
|
||||
val testLists = TestLists.default()
|
||||
iteratorTest(testLists)
|
||||
bulkMutationTest(testLists)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subListIterator() {
|
||||
val lists = TestSubLists.default()
|
||||
subListIteratorTest(lists)
|
||||
subListBulkMutationTest(lists)
|
||||
}
|
||||
|
||||
private fun <T> subListIteratorTest(lists: TestSubLists<T>) {
|
||||
var inputSubListCount = 0
|
||||
var mutableInputSubListCount = 0
|
||||
var subListCount = 0
|
||||
var mutableSubListCount = 0
|
||||
|
||||
lists.inputSub.iterator().forEach { _ -> inputSubListCount++ }
|
||||
lists.mutableInputSub.iterator().forEach { _ -> mutableInputSubListCount++ }
|
||||
lists.observableSub.iterator().forEach { _ -> subListCount++ }
|
||||
lists.mutableObservableSub.iterator().forEach { _ -> mutableSubListCount++ }
|
||||
|
||||
Assertions.assertTrue(inputSubListCount == lists.inputSub.size)
|
||||
Assertions.assertTrue(mutableInputSubListCount == lists.mutableInputSub.size)
|
||||
Assertions.assertTrue(subListCount == lists.observableSub.size)
|
||||
Assertions.assertTrue(mutableSubListCount == lists.mutableObservableSub.size)
|
||||
}
|
||||
|
||||
private fun <T> subListBulkMutationTest(lists: TestSubLists<T>) {
|
||||
// confirm mutations via iterator internally are tracked & reflected consistent with
|
||||
// kotlin mutable set implementations
|
||||
lists.mutableInputSub.removeAll(lists.inputSub)
|
||||
lists.mutableObservableSub.removeAll(lists.inputSub)
|
||||
|
||||
Assertions.assertTrue(lists.mutableObservableSub.containsAll(lists.mutableInputSub))
|
||||
Assertions.assertTrue(lists.mutableInputSub.containsAll(lists.mutableObservableSub))
|
||||
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableInputSub))
|
||||
Assertions.assertTrue(!lists.mutableInputSub.containsAll(lists.inputSub))
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableObservableSub))
|
||||
Assertions.assertTrue(!lists.mutableObservableSub.containsAll(lists.inputSub))
|
||||
|
||||
lists.mutableInputSub.addAll(lists.inputSub)
|
||||
lists.mutableObservableSub.addAll(lists.inputSub)
|
||||
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableInputSub))
|
||||
Assertions.assertTrue(lists.mutableInputSub.containsAll(lists.inputSub))
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableObservableSub))
|
||||
Assertions.assertTrue(lists.mutableObservableSub.containsAll(lists.inputSub))
|
||||
|
||||
lists.mutableInputSub.retainAll(lists.inputSub)
|
||||
lists.mutableObservableSub.retainAll(lists.inputSub)
|
||||
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableInputSub))
|
||||
Assertions.assertTrue(lists.mutableInputSub.containsAll(lists.inputSub))
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableObservableSub))
|
||||
Assertions.assertTrue(lists.mutableObservableSub.containsAll(lists.inputSub))
|
||||
Assertions.assertTrue(lists.mutableObservable.containsAll(lists.mutableInput))
|
||||
Assertions.assertTrue(lists.mutableObservable.size == lists.mutableInput.size)
|
||||
|
||||
// back to starting lists
|
||||
lists.mutableInputSub.clear()
|
||||
lists.mutableObservableSub.clear()
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableInputSub))
|
||||
Assertions.assertTrue(!lists.mutableInputSub.containsAll(lists.inputSub))
|
||||
Assertions.assertTrue(lists.inputSub.containsAll(lists.mutableObservableSub))
|
||||
Assertions.assertTrue(!lists.mutableObservableSub.containsAll(lists.inputSub))
|
||||
}
|
||||
|
||||
private fun iteratorTest(collections: TestCollections<*, *, *>) {
|
||||
var inputCount = 0
|
||||
var mutableInputCount = 0
|
||||
var collectionCount = 0
|
||||
var mutableCollectionCount = 0
|
||||
var emptyCollectionCount = 0
|
||||
var emptyMutableCollectionCount = 0
|
||||
|
||||
collections.input.iterator().forEach { _ -> inputCount++ }
|
||||
collections.mutableInput.iterator().forEach { _ -> mutableInputCount++ }
|
||||
collections.observable.iterator().forEach { _ -> collectionCount++ }
|
||||
collections.mutableObservable.iterator().forEach { _ -> mutableCollectionCount++ }
|
||||
collections.emptyObservable.iterator().forEach { _ -> emptyCollectionCount++ }
|
||||
collections.emptyMutableObservable.iterator().forEach { _ -> emptyMutableCollectionCount++ }
|
||||
|
||||
Assertions.assertTrue(inputCount == collections.input.size)
|
||||
Assertions.assertTrue(mutableInputCount == collections.mutableInput.size)
|
||||
Assertions.assertTrue(collectionCount == collections.observable.size)
|
||||
Assertions.assertTrue(mutableCollectionCount == collections.mutableObservable.size)
|
||||
Assertions.assertTrue(emptyCollectionCount == collections.emptyObservable.size)
|
||||
Assertions.assertTrue(emptyMutableCollectionCount == collections.emptyMutableObservable.size)
|
||||
}
|
||||
|
||||
private fun <T, C : Collection<T>, M : MutableCollection<T>> bulkMutationTest(
|
||||
collections: TestCollections<T, C, M>
|
||||
) {
|
||||
// confirm mutations via iterator internally are tracked & reflected consistent with
|
||||
// kotlin mutable set implementations
|
||||
collections.mutableInput.removeAll(collections.input)
|
||||
collections.mutableObservable.removeAll(collections.input)
|
||||
collections.emptyMutableObservable.removeAll(collections.input)
|
||||
iteratorTest(collections)
|
||||
|
||||
Assertions.assertTrue(collections.input.containsAll(collections.mutableInput))
|
||||
Assertions.assertTrue(!collections.mutableInput.containsAll(collections.input))
|
||||
Assertions.assertTrue(collections.input.containsAll(collections.mutableObservable))
|
||||
Assertions.assertTrue(!collections.mutableObservable.containsAll(collections.input))
|
||||
Assertions.assertTrue(collections.emptyMutableObservable.isEmpty())
|
||||
|
||||
collections.mutableInput.addAll(collections.input)
|
||||
collections.mutableObservable.addAll(collections.input)
|
||||
collections.emptyMutableObservable.addAll(collections.input)
|
||||
iteratorTest(collections)
|
||||
|
||||
Assertions.assertTrue(collections.input.containsAll(collections.mutableInput))
|
||||
Assertions.assertTrue(collections.mutableInput.containsAll(collections.input))
|
||||
Assertions.assertTrue(collections.input.containsAll(collections.mutableObservable))
|
||||
Assertions.assertTrue(collections.mutableObservable.containsAll(collections.input))
|
||||
Assertions.assertTrue(collections.emptyMutableObservable.isNotEmpty())
|
||||
|
||||
collections.mutableInput.retainAll(collections.input)
|
||||
collections.mutableObservable.retainAll(collections.input)
|
||||
collections.emptyMutableObservable.retainAll(collections.input)
|
||||
iteratorTest(collections)
|
||||
|
||||
Assertions.assertTrue(collections.input.containsAll(collections.mutableInput))
|
||||
Assertions.assertTrue(collections.mutableInput.containsAll(collections.input))
|
||||
Assertions.assertTrue(collections.input.containsAll(collections.mutableObservable))
|
||||
Assertions.assertTrue(collections.mutableObservable.containsAll(collections.input))
|
||||
Assertions.assertTrue(collections.emptyMutableObservable.isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mapIterator() {
|
||||
val testMaps = TestMaps.default()
|
||||
mapIteratorTest(testMaps)
|
||||
bulkMapMutationTest(testMaps)
|
||||
}
|
||||
|
||||
private fun <K, V> mapIteratorTest(maps: TestMaps<K, V>) {
|
||||
var inputCount = 0
|
||||
var mutableInputCount = 0
|
||||
var mapCount = 0
|
||||
var mutableMapCount = 0
|
||||
var emptyMapCount = 0
|
||||
var emptyMutableMapCount = 0
|
||||
|
||||
maps.input.iterator().forEach { _ -> inputCount++ }
|
||||
maps.mutableInput.iterator().forEach { _ -> mutableInputCount++ }
|
||||
maps.observable.iterator().forEach { _ -> mapCount++ }
|
||||
maps.mutableObservable.iterator().forEach { _ -> mutableMapCount++ }
|
||||
maps.emptyObservable.iterator().forEach { _ -> emptyMapCount++ }
|
||||
maps.emptyMutableObservable.iterator().forEach { _ -> emptyMutableMapCount++ }
|
||||
|
||||
Assertions.assertTrue(inputCount == maps.input.size)
|
||||
Assertions.assertTrue(mutableInputCount == maps.mutableInput.size)
|
||||
Assertions.assertTrue(mapCount == maps.observable.size)
|
||||
Assertions.assertTrue(mutableMapCount == maps.mutableObservable.size)
|
||||
Assertions.assertTrue(emptyMapCount == maps.emptyObservable.size)
|
||||
Assertions.assertTrue(emptyMutableMapCount == maps.emptyMutableObservable.size)
|
||||
}
|
||||
|
||||
private fun <K, V> bulkMapMutationTest(maps: TestMaps<K, V>) {
|
||||
removeIf(maps.mutableInput) { entry -> maps.input.any { match(entry, it) } }
|
||||
removeIf(maps.mutableObservable) { entry -> maps.input.any { match(entry, it) } }
|
||||
removeIf(maps.emptyMutableObservable) { entry -> maps.input.any { match(entry, it) } }
|
||||
mapIteratorTest(maps)
|
||||
|
||||
Assertions.assertTrue(containsAll(maps.input, maps.mutableInput))
|
||||
Assertions.assertTrue(!containsAll(maps.mutableInput, maps.input))
|
||||
Assertions.assertTrue(containsAll(maps.input, maps.mutableObservable))
|
||||
Assertions.assertTrue(!containsAll(maps.mutableObservable, maps.input))
|
||||
Assertions.assertTrue(maps.emptyMutableObservable.isEmpty())
|
||||
|
||||
maps.mutableInput.putAll(maps.input)
|
||||
maps.mutableObservable.putAll(maps.input)
|
||||
maps.emptyMutableObservable.putAll(maps.input)
|
||||
mapIteratorTest(maps)
|
||||
|
||||
Assertions.assertTrue(containsAll(maps.input, maps.mutableInput))
|
||||
Assertions.assertTrue(containsAll(maps.mutableInput, maps.input))
|
||||
Assertions.assertTrue(containsAll(maps.input, maps.mutableObservable))
|
||||
Assertions.assertTrue(containsAll(maps.mutableObservable, maps.input))
|
||||
Assertions.assertTrue(maps.emptyMutableObservable.isNotEmpty())
|
||||
|
||||
removeIf(maps.mutableInput) { entry -> !maps.input.any { match(entry, it) } }
|
||||
removeIf(maps.mutableObservable) { entry -> !maps.input.any { match(entry, it) } }
|
||||
removeIf(maps.emptyMutableObservable) { entry -> !maps.input.any { match(entry, it) } }
|
||||
mapIteratorTest(maps)
|
||||
|
||||
Assertions.assertTrue(containsAll(maps.input, maps.mutableInput))
|
||||
Assertions.assertTrue(containsAll(maps.mutableInput, maps.input))
|
||||
Assertions.assertTrue(containsAll(maps.input, maps.mutableObservable))
|
||||
Assertions.assertTrue(containsAll(maps.mutableObservable, maps.input))
|
||||
Assertions.assertTrue(maps.emptyMutableObservable.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun <K, V> match(entry: Entry<K, V>, other: Entry<K, V>): Boolean {
|
||||
return entry.key == other.key && entry.value == other.value
|
||||
}
|
||||
|
||||
private fun <K, V> removeIf(
|
||||
map: MutableMap<K, V>,
|
||||
predicate: (Entry<K, V>) -> Boolean,
|
||||
) {
|
||||
map.iterator().removeIf { predicate(it) }
|
||||
}
|
||||
|
||||
private fun <K, V> containsAll(map: Map<K, V>, other: Map<K, V>): Boolean {
|
||||
return when {
|
||||
other.isEmpty() -> true
|
||||
map.isEmpty() -> false
|
||||
else -> {
|
||||
map.forEach { entry ->
|
||||
if (!other.any { it.key == entry.key && it.value == entry.value }) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.loadMutableObservableListOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.loadObservableListOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.saveToNbt
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.loadMutableObservableMapOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.loadObservableMapOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.saveToNbt
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.loadMutableObservableSetOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.loadObservableSetOf
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.saveToNbt
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.collections.Map.Entry
|
||||
|
||||
object SerializationTests {
|
||||
|
||||
@Test
|
||||
fun setSerialization() {
|
||||
val sets = TestSets.default()
|
||||
|
||||
val saveElementHandler = { element: String -> NbtCompound().also { it.putString("element", element) } }
|
||||
|
||||
val setNbt = sets.observable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(setNbt.getInt("size") == sets.observable.size)
|
||||
|
||||
val setFromNbt = setNbt.loadObservableSetOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(setFromNbt.containsAll(sets.input))
|
||||
|
||||
val mutableSetNbt = sets.mutableObservable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(mutableSetNbt.getInt("size") == sets.mutableObservable.size)
|
||||
|
||||
val mutableSetFromNbt = mutableSetNbt.loadMutableObservableSetOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(mutableSetFromNbt.containsAll(sets.input))
|
||||
|
||||
val emptySetNbt = sets.emptyObservable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(emptySetNbt.getInt("size") == sets.emptyObservable.size)
|
||||
|
||||
val emptySetFromNbt = emptySetNbt.loadObservableSetOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(emptySetFromNbt.isEmpty())
|
||||
|
||||
val emptyMutableSetNbt = sets.emptyMutableObservable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(emptyMutableSetNbt.getInt("size") == sets.emptyMutableObservable.size)
|
||||
|
||||
val emptyMutableSetFromNbt = emptyMutableSetNbt.loadMutableObservableSetOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(emptyMutableSetFromNbt.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listSerialization() {
|
||||
val lists = TestLists.default()
|
||||
|
||||
val saveElementHandler = { element: String -> NbtCompound().also { it.putString("element", element) } }
|
||||
|
||||
val listNbt = lists.observable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(listNbt.getInt("size") == lists.observable.size)
|
||||
|
||||
val listFromNbt = listNbt.loadObservableListOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(listFromNbt.containsAll(lists.input))
|
||||
|
||||
val mutableListNbt = lists.mutableObservable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(mutableListNbt.getInt("size") == lists.mutableObservable.size)
|
||||
|
||||
val mutableListFromNbt = mutableListNbt.loadMutableObservableListOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(mutableListFromNbt.containsAll(lists.input))
|
||||
|
||||
val emptyListNbt = lists.emptyObservable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(emptyListNbt.getInt("size") == lists.emptyObservable.size)
|
||||
|
||||
val emptyListFromNbt = emptyListNbt.loadObservableListOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(emptyListFromNbt.isEmpty())
|
||||
|
||||
val emptyMutableListNbt = lists.emptyMutableObservable.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(emptyMutableListNbt.getInt("size") == lists.emptyMutableObservable.size)
|
||||
|
||||
val emptyMutableListFromNbt = emptyMutableListNbt.loadMutableObservableListOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(emptyMutableListFromNbt.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun subListSerialization() {
|
||||
val lists = TestSubLists.default()
|
||||
|
||||
val saveElementHandler = { element: String -> NbtCompound().also { it.putString("element", element) } }
|
||||
|
||||
val subListNbt = lists.observableSub.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(subListNbt.getInt("size") == lists.observableSub.size)
|
||||
|
||||
val subListFromNbt = subListNbt.loadObservableListOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(subListFromNbt.containsAll(lists.observableSub))
|
||||
|
||||
val mutableSubListNbt = lists.mutableObservableSub.saveToNbt { saveElementHandler(it) }
|
||||
Assertions.assertTrue(mutableSubListNbt.getInt("size") == lists.mutableObservableSub.size)
|
||||
|
||||
val mutableSubListFromNbt = mutableSubListNbt.loadMutableObservableListOf(
|
||||
elementKey = "element",
|
||||
loadElementHandler = { it.getString("element") },
|
||||
)
|
||||
Assertions.assertTrue(mutableSubListFromNbt.containsAll(lists.mutableObservableSub))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mapSerialization() {
|
||||
val maps = TestMaps.default()
|
||||
|
||||
val saveEntryHandler = { (key, value): Entry<Int, String> ->
|
||||
val entryNbt = NbtCompound()
|
||||
entryNbt.putInt("key", key)
|
||||
entryNbt.putString("value", value)
|
||||
entryNbt
|
||||
}
|
||||
|
||||
val mapNbt = maps.observable.saveToNbt { saveEntryHandler(it) }
|
||||
Assertions.assertTrue(mapNbt.getInt("size") == maps.observable.size)
|
||||
|
||||
val mapFromNbt = mapNbt.loadObservableMapOf(
|
||||
entryKey = "entry",
|
||||
loadEntryHandler = { it.getInt("key") to it.getString("value") },
|
||||
)
|
||||
Assertions.assertTrue(mapFromNbt.containsAll(maps.input))
|
||||
|
||||
val mutableMapNbt = maps.mutableObservable.saveToNbt { saveEntryHandler(it) }
|
||||
Assertions.assertTrue(mutableMapNbt.getInt("size") == maps.mutableObservable.size)
|
||||
|
||||
val mutableMapFromNbt = mutableMapNbt.loadMutableObservableMapOf(
|
||||
entryKey = "entry",
|
||||
loadEntryHandler = { it.getInt("key") to it.getString("value") },
|
||||
)
|
||||
Assertions.assertTrue(mutableMapFromNbt.containsAll(maps.input))
|
||||
|
||||
val emptyMapNbt = maps.emptyObservable.saveToNbt { saveEntryHandler(it) }
|
||||
Assertions.assertTrue(emptyMapNbt.getInt("size") == maps.emptyObservable.size)
|
||||
|
||||
val emptyMapFromNbt = emptyMapNbt.loadObservableMapOf(
|
||||
entryKey = "entry",
|
||||
loadEntryHandler = { it.getInt("key") to it.getString("value") },
|
||||
)
|
||||
Assertions.assertTrue(emptyMapFromNbt.isEmpty())
|
||||
|
||||
val emptyMutableMapNbt = maps.emptyMutableObservable.saveToNbt { saveEntryHandler(it) }
|
||||
Assertions.assertTrue(emptyMutableMapNbt.getInt("size") == maps.emptyMutableObservable.size)
|
||||
|
||||
val emptyMutableMapFromNbt = emptyMutableMapNbt.loadMutableObservableMapOf(
|
||||
entryKey = "entry",
|
||||
loadEntryHandler = { it.getInt("key") to it.getString("value") },
|
||||
)
|
||||
Assertions.assertTrue(emptyMutableMapFromNbt.isEmpty())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.MutableObservableCollection
|
||||
import com.cobblemon.mod.common.api.reactive.collections.ObservableCollection
|
||||
|
||||
interface TestCollections<T, C : Collection<T>, M : MutableCollection<T>> {
|
||||
|
||||
val input: C
|
||||
val mutableInput: M
|
||||
val observable: ObservableCollection<T, C>
|
||||
val mutableObservable: MutableObservableCollection<T, C>
|
||||
val emptyObservable: ObservableCollection<T, C>
|
||||
val emptyMutableObservable: MutableObservableCollection<T, C>
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.MutableObservableList
|
||||
import com.cobblemon.mod.common.api.reactive.collections.list.ObservableList
|
||||
|
||||
open class TestLists<T>(final override val input: List<T>) :
|
||||
TestCollections<T, List<T>, MutableList<T>> {
|
||||
|
||||
override val mutableInput: MutableList<T> = input.toMutableList()
|
||||
override val observable: ObservableList<T> = ObservableList(input)
|
||||
override val mutableObservable: MutableObservableList<T> = MutableObservableList(input)
|
||||
override val emptyObservable = ObservableList<T>(setOf())
|
||||
override val emptyMutableObservable = MutableObservableList<T>()
|
||||
|
||||
companion object {
|
||||
fun default() = testListsOf("zero", "one", "two", "three")
|
||||
fun defaultList() = listOf("zero", "one", "two", "three")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> testListsOf(vararg elements: T) = TestLists(listOf(*elements))
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.MutableObservableMap
|
||||
import com.cobblemon.mod.common.api.reactive.collections.map.ObservableMap
|
||||
|
||||
class TestMaps<K, V>(val input: Map<K, V>) {
|
||||
|
||||
val mutableInput: MutableMap<K, V> = input.toMutableMap()
|
||||
val observable: ObservableMap<K, V> = ObservableMap(input)
|
||||
val mutableObservable: MutableObservableMap<K, V> = MutableObservableMap(input)
|
||||
val emptyObservable = ObservableMap<K, V>(mapOf())
|
||||
val emptyMutableObservable = MutableObservableMap<K, V>()
|
||||
|
||||
companion object {
|
||||
fun default() = testMapsOf(0 to "zero", 1 to "one", 2 to "two", 3 to "three")
|
||||
fun defaultMap() = mapOf(0 to "zero", 1 to "one", 2 to "two", 3 to "three")
|
||||
}
|
||||
}
|
||||
|
||||
fun <K, V> testMapsOf(vararg pairs: Pair<K, V>) = TestMaps(mapOf(*pairs))
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.MutableObservableSet
|
||||
import com.cobblemon.mod.common.api.reactive.collections.set.ObservableSet
|
||||
|
||||
|
||||
class TestSets<T>(override val input: Set<T>) :
|
||||
TestCollections<T, Set<T>, MutableSet<T>> {
|
||||
|
||||
override val mutableInput: MutableSet<T> = input.toMutableSet()
|
||||
override val observable: ObservableSet<T> = ObservableSet(input)
|
||||
override val mutableObservable: MutableObservableSet<T> = MutableObservableSet(input)
|
||||
override val emptyObservable = ObservableSet<T>(setOf())
|
||||
override val emptyMutableObservable = MutableObservableSet<T>()
|
||||
|
||||
companion object {
|
||||
fun default() = testSetsOf("zero", "one", "two", "three")
|
||||
fun defaultSet() = setOf("zero", "one", "two", "three")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> testSetsOf(vararg elements: T) = TestSets(setOf(*elements))
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.cobblemon.mod.common.api.reactive
|
||||
|
||||
class TestSubLists<T>(
|
||||
input: List<T>,
|
||||
subIndices: Pair<Int, Int> = 1 to 3,
|
||||
) : TestLists<T>(input) {
|
||||
|
||||
val inputSub = this.input.subList(subIndices.first, subIndices.second)
|
||||
val mutableInputSub = mutableInput.subList(subIndices.first, subIndices.second)
|
||||
val observableSub = observable.subList(subIndices.first, subIndices.second)
|
||||
val mutableObservableSub = mutableObservable.subList(subIndices.first, subIndices.second)
|
||||
|
||||
companion object {
|
||||
fun default() = testSubListsOf("zero", "one", "two", "three")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> testSubListsOf(vararg elements: T) = TestSubLists(listOf(*elements))
|
||||
Reference in New Issue
Block a user