mirror of
https://github.com/corda/corda.git
synced 2025-02-21 17:56:54 +00:00
client, explorer: Address review comments
This commit is contained in:
parent
9a212a8714
commit
4da73e28c7
@ -1,19 +1,46 @@
|
|||||||
package com.r3corda.client.fxutils
|
package com.r3corda.client.fxutils
|
||||||
|
|
||||||
|
import co.paralleluniverse.common.util.VisibleForTesting
|
||||||
import javafx.collections.ListChangeListener
|
import javafx.collections.ListChangeListener
|
||||||
import javafx.collections.ObservableList
|
import javafx.collections.ObservableList
|
||||||
import javafx.collections.transformation.TransformationList
|
import javafx.collections.transformation.TransformationList
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.comparisons.compareValues
|
import kotlin.comparisons.compareValues
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ConcatenatedList] takes a list of lists and concatenates them. Any change to the underlying lists or the outer list
|
||||||
|
* is propagated as expected.
|
||||||
|
*/
|
||||||
class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : TransformationList<A, ObservableList<A>>(sourceList) {
|
class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : TransformationList<A, ObservableList<A>>(sourceList) {
|
||||||
class WrappedObservableList<A>(
|
// A wrapper for input lists so we hash differently even if a list is reused in the input.
|
||||||
|
@VisibleForTesting
|
||||||
|
internal class WrappedObservableList<A>(
|
||||||
val observableList: ObservableList<A>
|
val observableList: ObservableList<A>
|
||||||
)
|
)
|
||||||
private val indexMap = HashMap<WrappedObservableList<out A>, Pair<Int, ListChangeListener<A>>>()
|
// First let's clarify some concepts as it's easy to confuse which list we're handling where.
|
||||||
// Maps each list index to the offset of the next nested element
|
// Throughout the commentary and the code we will refer to the lists contained in the source list as "nested lists",
|
||||||
// Example: { {"a", "b"}, {"c"} } -> { 2, 3 }
|
// their elements "nested elements", whereas the containing list will be called "source list", its elements being
|
||||||
private val nestedIndexOffsets = ArrayList<Int>(sourceList.size)
|
// the nested lists. We will refer to the final concatenated list as "result list".
|
||||||
|
|
||||||
|
// We maintain two bookkeeping data-structures.
|
||||||
|
// 'indexMap' stores a mapping from nested lists to their respective source list indices and listeners.
|
||||||
|
// 'nestedIndexOffsets' stores for each nested list the index of the *next* nested element in the result list.
|
||||||
|
// We also have a helper function 'startingOffsetOf', which given an index of a nested list in the source list
|
||||||
|
// returns the index of its first element in the result list, or where it would be if it had one.
|
||||||
|
// For example:
|
||||||
|
// nested lists = { {"a", "b"}, {"c"}, {} }
|
||||||
|
// result list = { "a", "b", "c" }
|
||||||
|
// indexMap = [ {"c"} -> (1, listener),
|
||||||
|
// {} -> (2, listener),
|
||||||
|
// {"a", "b"} -> (0, listener) ]
|
||||||
|
// nestedIndexOffsets = { 2, 3, 3 }
|
||||||
|
// startingOffsetOf = { 0, 2, 3 }
|
||||||
|
// Note that similar to 'nestedIndexOffsets', 'startingOffsetOf' also isn't a one-to-one mapping because of
|
||||||
|
// potentially several empty nested lists.
|
||||||
|
@VisibleForTesting
|
||||||
|
internal val indexMap = HashMap<WrappedObservableList<out A>, Pair<Int, ListChangeListener<A>>>()
|
||||||
|
@VisibleForTesting
|
||||||
|
internal val nestedIndexOffsets = ArrayList<Int>(sourceList.size)
|
||||||
init {
|
init {
|
||||||
var offset = 0
|
var offset = 0
|
||||||
sourceList.forEachIndexed { index, observableList ->
|
sourceList.forEachIndexed { index, observableList ->
|
||||||
@ -24,26 +51,40 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startingOffsetOf(listIndex: Int): Int {
|
||||||
|
if (listIndex == 0) {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return nestedIndexOffsets[listIndex - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is where we create a listener for a *nested* list. Note that 'indexMap' doesn't need to be adjusted on any
|
||||||
|
// of these changes as the indices of nested lists don't change, just their contents.
|
||||||
private fun createListener(wrapped: WrappedObservableList<A>): ListChangeListener<A> {
|
private fun createListener(wrapped: WrappedObservableList<A>): ListChangeListener<A> {
|
||||||
val listener = ListChangeListener<A> { change ->
|
val listener = ListChangeListener<A> { change ->
|
||||||
beginChange()
|
beginChange()
|
||||||
while (change.next()) {
|
while (change.next()) {
|
||||||
if (change.wasPermutated()) {
|
if (change.wasPermutated()) {
|
||||||
val listIndex = indexMap[wrapped]!!.first
|
// If a nested list is permuted we simply offset the permutation by the startingOffsetOf the list.
|
||||||
|
// Note that we don't need to invalidate offsets.
|
||||||
|
val nestedListIndex = indexMap[wrapped]!!.first
|
||||||
val permutation = IntArray(change.to)
|
val permutation = IntArray(change.to)
|
||||||
if (listIndex >= firstInvalidatedPosition) {
|
val startingOffset = startingOffsetOf(nestedListIndex)
|
||||||
recalculateOffsets()
|
// firstTouched is the result list index of the beginning of the permutation.
|
||||||
}
|
|
||||||
val startingOffset = startingOffsetOf(listIndex)
|
|
||||||
val firstTouched = startingOffset + change.from
|
val firstTouched = startingOffset + change.from
|
||||||
|
// We first set the non-permuted indices.
|
||||||
for (i in 0..firstTouched - 1) {
|
for (i in 0..firstTouched - 1) {
|
||||||
permutation[i] = i
|
permutation[i] = i
|
||||||
}
|
}
|
||||||
for (i in startingOffset + change.from..startingOffset + change.to - 1) {
|
// Then the permuted ones.
|
||||||
|
for (i in firstTouched .. startingOffset + change.to - 1) {
|
||||||
permutation[startingOffset + i] = change.getPermutation(i)
|
permutation[startingOffset + i] = change.getPermutation(i)
|
||||||
}
|
}
|
||||||
nextPermutation(firstTouched, startingOffset + change.to, permutation)
|
nextPermutation(firstTouched, startingOffset + change.to, permutation)
|
||||||
} else if (change.wasUpdated()) {
|
} else if (change.wasUpdated()) {
|
||||||
|
// If a nested element is updated we simply propagate the update by offsetting the nested element index
|
||||||
|
// by the startingOffsetOf the nested list.
|
||||||
val listIndex = indexMap[wrapped]!!.first
|
val listIndex = indexMap[wrapped]!!.first
|
||||||
val startingOffset = startingOffsetOf(listIndex)
|
val startingOffset = startingOffsetOf(listIndex)
|
||||||
for (i in change.from..change.to - 1) {
|
for (i in change.from..change.to - 1) {
|
||||||
@ -51,14 +92,21 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (change.wasRemoved()) {
|
if (change.wasRemoved()) {
|
||||||
|
// If nested elements are removed we again simply offset the change. We also need to invalidate
|
||||||
|
// 'nestedIndexOffsets' unless we removed the same number of elements as we added
|
||||||
val listIndex = indexMap[wrapped]!!.first
|
val listIndex = indexMap[wrapped]!!.first
|
||||||
invalidateOffsets(listIndex)
|
if (!(change.wasAdded() && change.addedSize == change.removedSize)) {
|
||||||
|
invalidateOffsets(listIndex)
|
||||||
|
}
|
||||||
val startingOffset = startingOffsetOf(listIndex)
|
val startingOffset = startingOffsetOf(listIndex)
|
||||||
nextRemove(startingOffset + change.from, change.removed)
|
nextRemove(startingOffset + change.from, change.removed)
|
||||||
}
|
}
|
||||||
if (change.wasAdded()) {
|
if (change.wasAdded()) {
|
||||||
|
// Similar logic to remove.
|
||||||
val listIndex = indexMap[wrapped]!!.first
|
val listIndex = indexMap[wrapped]!!.first
|
||||||
invalidateOffsets(listIndex)
|
if (!(change.wasRemoved() && change.addedSize == change.removedSize)) {
|
||||||
|
invalidateOffsets(listIndex)
|
||||||
|
}
|
||||||
val startingOffset = startingOffsetOf(listIndex)
|
val startingOffset = startingOffsetOf(listIndex)
|
||||||
nextAdd(startingOffset + change.from, startingOffset + change.to)
|
nextAdd(startingOffset + change.from, startingOffset + change.to)
|
||||||
}
|
}
|
||||||
@ -71,23 +119,28 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
return listener
|
return listener
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracks the first position where the *nested* offset is invalid
|
// This is where we handle changes to the *source* list.
|
||||||
private var firstInvalidatedPosition = sourceList.size
|
|
||||||
|
|
||||||
override fun sourceChanged(change: ListChangeListener.Change<out ObservableList<A>>) {
|
override fun sourceChanged(change: ListChangeListener.Change<out ObservableList<A>>) {
|
||||||
beginChange()
|
beginChange()
|
||||||
while (change.next()) {
|
while (change.next()) {
|
||||||
if (change.wasPermutated()) {
|
if (change.wasPermutated()) {
|
||||||
// Update indexMap
|
// If the source list was permuted we adjust 'nestedIndexOffsets' and translate the permutation to apply
|
||||||
|
// to the nested elements.
|
||||||
|
// For example:
|
||||||
|
// original list: { {"a", "b"}, {"c", "d"}, {} }
|
||||||
|
// original permutation: { 2, 1, 0 }
|
||||||
|
// permuted list: { {}, {"c", "d"}, {"a", "b"} }
|
||||||
|
// translated permutation: { 2, 3, 0, 1 }
|
||||||
|
|
||||||
|
// First we apply the permutation to the 'indexMap'
|
||||||
val iterator = indexMap.iterator()
|
val iterator = indexMap.iterator()
|
||||||
for (entry in iterator) {
|
for (entry in iterator) {
|
||||||
val (wrapped, pair) = entry
|
val (index, listener) = entry.value
|
||||||
val (index, listener) = pair
|
|
||||||
if (index >= change.from && index < change.to) {
|
if (index >= change.from && index < change.to) {
|
||||||
entry.setValue(Pair(change.getPermutation(index), listener))
|
entry.setValue(Pair(change.getPermutation(index), listener))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calculate the permuted sublist of nestedIndexOffsets
|
// We apply the permutation to the relevant part of 'nestedIndexOffsets'.
|
||||||
val newSubNestedIndexOffsets = IntArray(change.to - change.from)
|
val newSubNestedIndexOffsets = IntArray(change.to - change.from)
|
||||||
val firstTouched = if (change.from == 0) 0 else nestedIndexOffsets[change.from - 1]
|
val firstTouched = if (change.from == 0) 0 else nestedIndexOffsets[change.from - 1]
|
||||||
var currentOffset = firstTouched
|
var currentOffset = firstTouched
|
||||||
@ -95,6 +148,7 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
currentOffset += source[change.from + i].size
|
currentOffset += source[change.from + i].size
|
||||||
newSubNestedIndexOffsets[i] = currentOffset
|
newSubNestedIndexOffsets[i] = currentOffset
|
||||||
}
|
}
|
||||||
|
// Now we create the permutation array for the result list.
|
||||||
val concatenatedPermutation = IntArray(newSubNestedIndexOffsets.last())
|
val concatenatedPermutation = IntArray(newSubNestedIndexOffsets.last())
|
||||||
// Set the non-permuted part
|
// Set the non-permuted part
|
||||||
var offset = 0
|
var offset = 0
|
||||||
@ -124,13 +178,14 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
throw UnsupportedOperationException("Updates not supported")
|
throw UnsupportedOperationException("Updates not supported")
|
||||||
} else {
|
} else {
|
||||||
if (change.wasRemoved()) {
|
if (change.wasRemoved()) {
|
||||||
// Update indexMap
|
// If nested lists were removed we iterate over 'indexMap' and adjust the indices accordingly,
|
||||||
|
// remove listeners and remove relevant mappings as well. We also invalidate nested offsets.
|
||||||
val iterator = indexMap.iterator()
|
val iterator = indexMap.iterator()
|
||||||
for (entry in iterator) {
|
for (entry in iterator) {
|
||||||
val (wrapped, pair) = entry
|
val (wrapped, pair) = entry
|
||||||
val (index, listener) = pair
|
val (index, listener) = pair
|
||||||
val removeEnd = change.from + change.removedSize
|
|
||||||
if (index >= change.from) {
|
if (index >= change.from) {
|
||||||
|
val removeEnd = change.from + change.removedSize
|
||||||
if (index < removeEnd) {
|
if (index < removeEnd) {
|
||||||
wrapped.observableList.removeListener(listener)
|
wrapped.observableList.removeListener(listener)
|
||||||
iterator.remove()
|
iterator.remove()
|
||||||
@ -162,13 +217,9 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
indexMap[wrapped] = Pair(change.from + sublistIndex, createListener(wrapped))
|
indexMap[wrapped] = Pair(change.from + sublistIndex, createListener(wrapped))
|
||||||
}
|
}
|
||||||
invalidateOffsets(change.from)
|
invalidateOffsets(change.from)
|
||||||
|
// We recalculate offsets early as we need the range anyway.
|
||||||
recalculateOffsets()
|
recalculateOffsets()
|
||||||
nextAdd(startingOffsetOf(change.from), nestedIndexOffsets[change.to - 1])
|
nextAdd(startingOffsetOf(change.from), nestedIndexOffsets[change.to - 1])
|
||||||
for (i in change.from .. change.to - 1) {
|
|
||||||
source[i].addListener { change: ListChangeListener.Change<out A> ->
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recalculateOffsets()
|
recalculateOffsets()
|
||||||
@ -176,18 +227,12 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
|||||||
endChange()
|
endChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracks the first position where the *nested* offset is invalid
|
||||||
|
private var firstInvalidatedPosition = sourceList.size
|
||||||
private fun invalidateOffsets(index: Int) {
|
private fun invalidateOffsets(index: Int) {
|
||||||
firstInvalidatedPosition = Math.min(firstInvalidatedPosition, index)
|
firstInvalidatedPosition = Math.min(firstInvalidatedPosition, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startingOffsetOf(listIndex: Int): Int {
|
|
||||||
if (listIndex == 0) {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
return nestedIndexOffsets[listIndex - 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recalculateOffsets() {
|
private fun recalculateOffsets() {
|
||||||
if (firstInvalidatedPosition < source.size) {
|
if (firstInvalidatedPosition < source.size) {
|
||||||
val firstInvalid = firstInvalidatedPosition
|
val firstInvalid = firstInvalidatedPosition
|
||||||
|
@ -8,6 +8,7 @@ import javafx.collections.ObservableList
|
|||||||
import javafx.collections.transformation.TransformationList
|
import javafx.collections.transformation.TransformationList
|
||||||
import org.eclipse.jetty.server.Authentication
|
import org.eclipse.jetty.server.Authentication
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [FlattenedList] flattens the passed in list of [ObservableValue]s so that changes in individual updates to the values
|
* [FlattenedList] flattens the passed in list of [ObservableValue]s so that changes in individual updates to the values
|
||||||
@ -106,7 +107,7 @@ class FlattenedList<A>(val sourceList: ObservableList<out ObservableValue<out A>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
endChange()
|
endChange()
|
||||||
require(sourceList.size == indexMap.size)
|
assertEquals(sourceList.size, indexMap.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(index: Int) = sourceList.get(index).value
|
override fun get(index: Int) = sourceList.get(index).value
|
||||||
|
@ -17,6 +17,11 @@ class MapValuesList<K, A, C> private constructor(
|
|||||||
) : ObservableList<C> by exposedList {
|
) : ObservableList<C> by exposedList {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* [create] is the factory of [MapValuesList].
|
||||||
|
* @param sourceMap The source map.
|
||||||
|
* @param assemble The function to be called for map each entry to construct the final list elements.
|
||||||
|
*/
|
||||||
fun <K, A, C> create(sourceMap: ObservableMap<K, A>, assemble: (Map.Entry<K, A>) -> C): MapValuesList<K, A, C> {
|
fun <K, A, C> create(sourceMap: ObservableMap<K, A>, assemble: (Map.Entry<K, A>) -> C): MapValuesList<K, A, C> {
|
||||||
val backingList = FXCollections.observableArrayList<Map.Entry<K, A>>(sourceMap.entries.sortedBy { it.key!!.hashCode() })
|
val backingList = FXCollections.observableArrayList<Map.Entry<K, A>>(sourceMap.entries.sortedBy { it.key!!.hashCode() })
|
||||||
return MapValuesList(sourceMap, backingList, backingList.map { assemble(it) })
|
return MapValuesList(sourceMap, backingList, backingList.map { assemble(it) })
|
||||||
|
@ -9,6 +9,7 @@ import java.util.*
|
|||||||
/**
|
/**
|
||||||
* This is a variant of [EasyBind.map] where the mapped list is backed, therefore the mapping function will only be run
|
* This is a variant of [EasyBind.map] where the mapped list is backed, therefore the mapping function will only be run
|
||||||
* when an element is inserted or updated.
|
* when an element is inserted or updated.
|
||||||
|
* Use this instead of [EasyBind.map] to trade off memory vs CPU, or if (god forbid) the mapped function is side-effecting.
|
||||||
*/
|
*/
|
||||||
class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : TransformationList<B, A>(list) {
|
class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : TransformationList<B, A>(list) {
|
||||||
private val backingList = ArrayList<B>(list.size)
|
private val backingList = ArrayList<B>(list.size)
|
||||||
@ -23,12 +24,14 @@ class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : Transf
|
|||||||
beginChange()
|
beginChange()
|
||||||
while (change.next()) {
|
while (change.next()) {
|
||||||
if (change.wasPermutated()) {
|
if (change.wasPermutated()) {
|
||||||
|
// Note how we don't re-run the mapping function on a permutation. If we supported mapIndexed we would
|
||||||
|
// have to.
|
||||||
val from = change.from
|
val from = change.from
|
||||||
val to = change.to
|
val to = change.to
|
||||||
val permutation = IntArray(to, { change.getPermutation(it) })
|
val permutation = IntArray(to) { change.getPermutation(it) }
|
||||||
val permutedSubList = ArrayList<B?>(to - from)
|
val permutedSubList = ArrayList<B?>(to - from)
|
||||||
permutedSubList.addAll(Collections.nCopies(to - from, null))
|
permutedSubList.addAll(Collections.nCopies(to - from, null))
|
||||||
for (i in 0 .. (to - from - 1)) {
|
for (i in 0.until(to - from)) {
|
||||||
permutedSubList[permutation[from + i]] = backingList[from + i]
|
permutedSubList[permutation[from + i]] = backingList[from + i]
|
||||||
}
|
}
|
||||||
permutedSubList.forEachIndexed { i, element ->
|
permutedSubList.forEachIndexed { i, element ->
|
||||||
@ -42,7 +45,7 @@ class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : Transf
|
|||||||
if (change.wasRemoved()) {
|
if (change.wasRemoved()) {
|
||||||
val removePosition = change.from
|
val removePosition = change.from
|
||||||
val removed = ArrayList<B>(change.removedSize)
|
val removed = ArrayList<B>(change.removedSize)
|
||||||
for (i in 0 .. change.removedSize - 1) {
|
for (i in 0.until(change.removedSize)) {
|
||||||
removed.add(backingList.removeAt(removePosition))
|
removed.add(backingList.removeAt(removePosition))
|
||||||
}
|
}
|
||||||
nextRemove(change.from, removed)
|
nextRemove(change.from, removed)
|
||||||
@ -50,7 +53,7 @@ class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : Transf
|
|||||||
if (change.wasAdded()) {
|
if (change.wasAdded()) {
|
||||||
val addStart = change.from
|
val addStart = change.from
|
||||||
val addEnd = change.to
|
val addEnd = change.to
|
||||||
for (i in addStart .. addEnd - 1) {
|
for (i in addStart.until(addEnd)) {
|
||||||
backingList.add(i, function(change.list[i]))
|
backingList.add(i, function(change.list[i]))
|
||||||
}
|
}
|
||||||
nextAdd(addStart, addEnd)
|
nextAdd(addStart, addEnd)
|
||||||
|
@ -12,6 +12,12 @@ import rx.Observable
|
|||||||
* Simple utilities for converting an [rx.Observable] into a javafx [ObservableValue]/[ObservableList]
|
* Simple utilities for converting an [rx.Observable] into a javafx [ObservableValue]/[ObservableList]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [foldToObservableValue] takes an [rx.Observable] stream and creates an [ObservableValue] out of it.
|
||||||
|
* @param initial The initial value of the returned observable.
|
||||||
|
* @param folderFun The transformation function to be called on the observable value when a new element is emitted on
|
||||||
|
* the stream.
|
||||||
|
*/
|
||||||
fun <A, B> Observable<A>.foldToObservableValue(initial: B, folderFun: (A, B) -> B): ObservableValue<B> {
|
fun <A, B> Observable<A>.foldToObservableValue(initial: B, folderFun: (A, B) -> B): ObservableValue<B> {
|
||||||
val result = SimpleObjectProperty<B>(initial)
|
val result = SimpleObjectProperty<B>(initial)
|
||||||
subscribe {
|
subscribe {
|
||||||
@ -22,6 +28,13 @@ fun <A, B> Observable<A>.foldToObservableValue(initial: B, folderFun: (A, B) ->
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [foldToObservableList] takes an [rx.Observable] stream and creates an [ObservableList] out of it, while maintaining
|
||||||
|
* an accumulator.
|
||||||
|
* @param initialAccumulator The initial value of the accumulator.
|
||||||
|
* @param folderFun The transformation function to be called on the observable list when a new element is emitted on
|
||||||
|
* the stream, which should modify the list as needed.
|
||||||
|
*/
|
||||||
fun <A, B, C> Observable<A>.foldToObservableList(
|
fun <A, B, C> Observable<A>.foldToObservableList(
|
||||||
initialAccumulator: C, folderFun: (A, C, ObservableList<B>) -> C
|
initialAccumulator: C, folderFun: (A, C, ObservableList<B>) -> C
|
||||||
): ObservableList<B> {
|
): ObservableList<B> {
|
||||||
@ -39,14 +52,21 @@ fun <A, B, C> Observable<A>.foldToObservableList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This variant simply exposes all events in the list, in order of arrival.
|
* [recordInSequence] records incoming events on the [rx.Observable] in sequence.
|
||||||
*/
|
*/
|
||||||
fun <A> Observable<A>.foldToObservableList(): ObservableList<A> {
|
fun <A> Observable<A>.recordInSequence(): ObservableList<A> {
|
||||||
return foldToObservableList(Unit) { newElement, _unit, list ->
|
return foldToObservableList(Unit) { newElement, _unit, list ->
|
||||||
list.add(newElement)
|
list.add(newElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [foldToObservableMap] takes an [rx.Observable] stream and creates an [ObservableMap] out of it, while maintaining
|
||||||
|
* an accumulator.
|
||||||
|
* @param initialAccumulator The initial value of the accumulator.
|
||||||
|
* @param folderFun The transformation function to be called on the observable map when a new element is emitted on
|
||||||
|
* the stream, which should modify the map as needed.
|
||||||
|
*/
|
||||||
fun <A, B, K, C> Observable<A>.foldToObservableMap(
|
fun <A, B, K, C> Observable<A>.foldToObservableMap(
|
||||||
initialAccumulator: C, folderFun: (A, C, ObservableMap<K, B>) -> C
|
initialAccumulator: C, folderFun: (A, C, ObservableMap<K, B>) -> C
|
||||||
): ObservableMap<K, out B> {
|
): ObservableMap<K, out B> {
|
||||||
@ -68,7 +88,7 @@ fun <A, B, K, C> Observable<A>.foldToObservableMap(
|
|||||||
* @param toKey Function retrieving the key to associate with.
|
* @param toKey Function retrieving the key to associate with.
|
||||||
* @param merge The function to be called if there is an existing element at the key.
|
* @param merge The function to be called if there is an existing element at the key.
|
||||||
*/
|
*/
|
||||||
fun <A, K> Observable<A>.foldToObservableMap(
|
fun <A, K> Observable<A>.recordAsAssociation(
|
||||||
toKey: (A) -> K,
|
toKey: (A) -> K,
|
||||||
merge: (K, oldValue: A, newValue: A) -> A = { _key, _oldValue, newValue -> newValue }
|
merge: (K, oldValue: A, newValue: A) -> A = { _key, _oldValue, newValue -> newValue }
|
||||||
): ObservableMap<K, out A> {
|
): ObservableMap<K, out A> {
|
||||||
|
@ -28,9 +28,17 @@ fun <A, B> ObservableValue<out A>.map(function: (A) -> B): ObservableValue<B> =
|
|||||||
/**
|
/**
|
||||||
* val dogs: ObservableList<Dog> = (..)
|
* val dogs: ObservableList<Dog> = (..)
|
||||||
* val dogOwners: ObservableList<Person> = dogs.map { it.owner }
|
* val dogOwners: ObservableList<Person> = dogs.map { it.owner }
|
||||||
|
*
|
||||||
|
* @param cached If true the results of the mapped function are cached in a backing list. If false each get() will
|
||||||
|
* re-run the function.
|
||||||
*/
|
*/
|
||||||
fun <A, B> ObservableList<out A>.map(function: (A) -> B): ObservableList<B> = MappedList(this, function)
|
fun <A, B> ObservableList<out A>.map(cached: Boolean = true, function: (A) -> B): ObservableList<B> {
|
||||||
fun <A, B> ObservableList<out A>.mapNonBacked(function: (A) -> B): ObservableList<B> = EasyBind.map(this, function)
|
if (cached) {
|
||||||
|
return MappedList(this, function)
|
||||||
|
} else {
|
||||||
|
return EasyBind.map(this, function)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* val aliceHeight: ObservableValue<Long> = (..)
|
* val aliceHeight: ObservableValue<Long> = (..)
|
||||||
@ -126,12 +134,14 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
|
|||||||
fun <A> ObservableList<out ObservableValue<out A>>.flatten(): ObservableList<A> = FlattenedList(this)
|
fun <A> ObservableList<out ObservableValue<out A>>.flatten(): ObservableList<A> = FlattenedList(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* data class Person(val height: ObservableValue<Long>)
|
||||||
* val people: List<Person> = listOf(alice, bob)
|
* val people: List<Person> = listOf(alice, bob)
|
||||||
* val heights: ObservableList<Long> = people.map(Person::height).sequence()
|
* val heights: ObservableList<Long> = people.map(Person::height).sequence()
|
||||||
*/
|
*/
|
||||||
fun <A> List<ObservableValue<out A>>.sequence(): ObservableList<A> = FlattenedList(FXCollections.observableArrayList(this))
|
fun <A> Collection<ObservableValue<out A>>.sequence(): ObservableList<A> = FlattenedList(FXCollections.observableArrayList(this))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* data class Person(val height: Long)
|
||||||
* val people: ObservableList<Person> = (..)
|
* val people: ObservableList<Person> = (..)
|
||||||
* val nameToHeight: ObservableMap<String, Long> = people.associateBy(Person::name) { name, person -> person.height }
|
* val nameToHeight: ObservableMap<String, Long> = people.associateBy(Person::name) { name, person -> person.height }
|
||||||
*/
|
*/
|
||||||
@ -209,6 +219,7 @@ fun <A> ObservableList<ObservableList<A>>.concatenate(): ObservableList<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* data class Person(val name: String, val managerName: String)
|
||||||
* val people: ObservableList<Person> = (..)
|
* val people: ObservableList<Person> = (..)
|
||||||
* val managerEmployeeMapping: ObservableList<Pair<Person, ObservableList<Person>>> =
|
* val managerEmployeeMapping: ObservableList<Pair<Person, ObservableList<Person>>> =
|
||||||
* people.leftOuterJoin(people, Person::name, Person::managerName) { manager, employees -> Pair(manager, employees) }
|
* people.leftOuterJoin(people, Person::name, Person::managerName) { manager, employees -> Pair(manager, employees) }
|
||||||
@ -225,6 +236,18 @@ fun <A : Any, B : Any, C, K : Any> ObservableList<A>.leftOuterJoin(
|
|||||||
}.concatenate()
|
}.concatenate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* data class Person(name: String, favouriteSpecies: Species)
|
||||||
|
* data class Animal(name: String, species: Species)
|
||||||
|
* val people: ObservableList<Person> = (..)
|
||||||
|
* val animals: ObservableList<Animal> = (..)
|
||||||
|
* val peopleToFavouriteAnimals: ObservableMap<Species, Pair<ObservableList<Person>, ObservableList<Animal>>> =
|
||||||
|
* people.leftOuterJoin(animals, Person::favouriteSpecies, Animal::species)
|
||||||
|
*
|
||||||
|
* This is the most general left join, given a joining key it returns for each key a pair of relevant elements from the
|
||||||
|
* left and right tables. It is "left outer" in the sense that all members of the left table are guaranteed to be in
|
||||||
|
* the result, but this may not be the case for the right table.
|
||||||
|
*/
|
||||||
fun <A : Any, B : Any, K : Any> ObservableList<A>.leftOuterJoin(
|
fun <A : Any, B : Any, K : Any> ObservableList<A>.leftOuterJoin(
|
||||||
rightTable: ObservableList<B>,
|
rightTable: ObservableList<B>,
|
||||||
leftToJoinKey: (A) -> K,
|
leftToJoinKey: (A) -> K,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.r3corda.client.model
|
package com.r3corda.client.model
|
||||||
|
|
||||||
import com.r3corda.client.fxutils.foldToObservableList
|
import com.r3corda.client.fxutils.foldToObservableList
|
||||||
|
import com.r3corda.client.fxutils.recordInSequence
|
||||||
import com.r3corda.contracts.asset.Cash
|
import com.r3corda.contracts.asset.Cash
|
||||||
import com.r3corda.core.contracts.ContractState
|
import com.r3corda.core.contracts.ContractState
|
||||||
import com.r3corda.core.contracts.StateAndRef
|
import com.r3corda.core.contracts.StateAndRef
|
||||||
|
@ -5,6 +5,7 @@ import com.r3corda.client.fxutils.getObservableValue
|
|||||||
import com.r3corda.core.contracts.ContractState
|
import com.r3corda.core.contracts.ContractState
|
||||||
import com.r3corda.core.contracts.StateAndRef
|
import com.r3corda.core.contracts.StateAndRef
|
||||||
import com.r3corda.core.contracts.StateRef
|
import com.r3corda.core.contracts.StateRef
|
||||||
|
import com.r3corda.client.fxutils.recordInSequence
|
||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import com.r3corda.core.protocols.StateMachineRunId
|
import com.r3corda.core.protocols.StateMachineRunId
|
||||||
import com.r3corda.core.transactions.SignedTransaction
|
import com.r3corda.core.transactions.SignedTransaction
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
package com.r3corda.client.fxutils
|
package com.r3corda.client.fxutils
|
||||||
|
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
|
import javafx.collections.ObservableList
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
|
||||||
class AggregatedListTest {
|
class AggregatedListTest {
|
||||||
|
|
||||||
var sourceList = FXCollections.observableArrayList<Int>()
|
lateinit var sourceList: ObservableList<Int>
|
||||||
var aggregatedList = AggregatedList(sourceList, { it % 3 }) { mod3, group -> Pair(mod3, group) }
|
lateinit var aggregatedList: ObservableList<Pair<Int, ObservableList<Int>>>
|
||||||
var replayedList = ReplayedList(aggregatedList)
|
lateinit var replayedList: ObservableList<Pair<Int, ObservableList<Int>>>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@ -20,22 +22,22 @@ class AggregatedListTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedList.size == 0) { "Aggregation is empty is source list is" }
|
assertEquals(replayedList.size, 0)
|
||||||
|
|
||||||
sourceList.add(9)
|
sourceList.add(9)
|
||||||
require(replayedList.size == 1) { "Aggregation list has one element if one was added to source list" }
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0]!!.first == 0)
|
assertEquals(replayedList[0]!!.first, 0)
|
||||||
|
|
||||||
sourceList.add(8)
|
sourceList.add(8)
|
||||||
require(replayedList.size == 2) { "Aggregation list has two elements if two were added to source list with different keys" }
|
assertEquals(replayedList.size, 2)
|
||||||
|
|
||||||
sourceList.add(6)
|
sourceList.add(6)
|
||||||
require(replayedList.size == 2) { "Aggregation list's size doesn't change if element with existing key is added" }
|
assertEquals(replayedList.size, 2)
|
||||||
|
|
||||||
replayedList.forEach {
|
replayedList.forEach {
|
||||||
when (it.first) {
|
when (it.first) {
|
||||||
0 -> require(it.second.toSet() == setOf(6, 9))
|
0 -> assertEquals(it.second.toSet(), setOf(6, 9))
|
||||||
2 -> require(it.second.size == 1)
|
2 -> assertEquals(it.second.size, 1)
|
||||||
else -> fail("No aggregation expected with key ${it.first}")
|
else -> fail("No aggregation expected with key ${it.first}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,51 +47,51 @@ class AggregatedListTest {
|
|||||||
fun removeWorks() {
|
fun removeWorks() {
|
||||||
sourceList.addAll(0, 1, 2, 3, 4)
|
sourceList.addAll(0, 1, 2, 3, 4)
|
||||||
|
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
replayedList.forEach {
|
replayedList.forEach {
|
||||||
when (it.first) {
|
when (it.first) {
|
||||||
0 -> require(it.second.toSet() == setOf(0, 3))
|
0 -> assertEquals(it.second.toSet(), setOf(0, 3))
|
||||||
1 -> require(it.second.toSet() == setOf(1, 4))
|
1 -> assertEquals(it.second.toSet(), setOf(1, 4))
|
||||||
2 -> require(it.second.toSet() == setOf(2))
|
2 -> assertEquals(it.second.toSet(), setOf(2))
|
||||||
else -> fail("No aggregation expected with key ${it.first}")
|
else -> fail("No aggregation expected with key ${it.first}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceList.remove(4)
|
sourceList.remove(4)
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
replayedList.forEach {
|
replayedList.forEach {
|
||||||
when (it.first) {
|
when (it.first) {
|
||||||
0 -> require(it.second.toSet() == setOf(0, 3))
|
0 -> assertEquals(it.second.toSet(), setOf(0, 3))
|
||||||
1 -> require(it.second.toSet() == setOf(1))
|
1 -> assertEquals(it.second.toSet(), setOf(1))
|
||||||
2 -> require(it.second.toSet() == setOf(2))
|
2 -> assertEquals(it.second.toSet(), setOf(2))
|
||||||
else -> fail("No aggregation expected with key ${it.first}")
|
else -> fail("No aggregation expected with key ${it.first}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceList.remove(2, 4)
|
sourceList.remove(2, 4)
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
replayedList.forEach {
|
replayedList.forEach {
|
||||||
when (it.first) {
|
when (it.first) {
|
||||||
0 -> require(it.second.toSet() == setOf(0))
|
0 -> assertEquals(it.second.toSet(), setOf(0))
|
||||||
1 -> require(it.second.toSet() == setOf(1))
|
1 -> assertEquals(it.second.toSet(), setOf(1))
|
||||||
else -> fail("No aggregation expected with key ${it.first}")
|
else -> fail("No aggregation expected with key ${it.first}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceList.removeAll(0, 1)
|
sourceList.removeAll(0, 1)
|
||||||
require(replayedList.size == 0)
|
assertEquals(replayedList.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleElementsWithSameHashWorks() {
|
fun multipleElementsWithSameHashWorks() {
|
||||||
sourceList.addAll(0, 0)
|
sourceList.addAll(0, 0)
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
replayedList.forEach {
|
replayedList.forEach {
|
||||||
when (it.first) {
|
when (it.first) {
|
||||||
0 -> {
|
0 -> {
|
||||||
require(it.second.size == 2)
|
assertEquals(it.second.size, 2)
|
||||||
require(it.second[0] == 0)
|
assertEquals(it.second[0], 0)
|
||||||
require(it.second[1] == 0)
|
assertEquals(it.second[1], 0)
|
||||||
}
|
}
|
||||||
else -> fail("No aggregation expected with key ${it.first}")
|
else -> fail("No aggregation expected with key ${it.first}")
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
package com.r3corda.client.fxutils
|
package com.r3corda.client.fxutils
|
||||||
|
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
|
import javafx.collections.ObservableList
|
||||||
|
import javafx.collections.ObservableMap
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class AssociatedListTest {
|
class AssociatedListTest {
|
||||||
|
|
||||||
var sourceList = FXCollections.observableArrayList(0)
|
lateinit var sourceList: ObservableList<Int>
|
||||||
var associatedList = AssociatedList(sourceList, { it % 3 }) { mod3, number -> number }
|
lateinit var associatedList: ObservableMap<Int, Int>
|
||||||
var replayedMap = ReplayedMap(associatedList)
|
lateinit var replayedMap: ObservableMap<Int, Int>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@ -19,55 +22,55 @@ class AssociatedListTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedMap.size == 1)
|
assertEquals(replayedMap.size, 1)
|
||||||
require(replayedMap[0] == 0)
|
assertEquals(replayedMap[0], 0)
|
||||||
|
|
||||||
sourceList.add(2)
|
sourceList.add(2)
|
||||||
require(replayedMap.size == 2)
|
assertEquals(replayedMap.size, 2)
|
||||||
require(replayedMap[0] == 0)
|
assertEquals(replayedMap[0], 0)
|
||||||
require(replayedMap[2] == 2)
|
assertEquals(replayedMap[2], 2)
|
||||||
|
|
||||||
sourceList.add(0, 4)
|
sourceList.add(0, 4)
|
||||||
require(replayedMap.size == 3)
|
assertEquals(replayedMap.size, 3)
|
||||||
require(replayedMap[0] == 0)
|
assertEquals(replayedMap[0], 0)
|
||||||
require(replayedMap[2] == 2)
|
assertEquals(replayedMap[2], 2)
|
||||||
require(replayedMap[1] == 4)
|
assertEquals(replayedMap[1], 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeWorks() {
|
fun removeWorks() {
|
||||||
sourceList.addAll(2, 4)
|
sourceList.addAll(2, 4)
|
||||||
require(replayedMap.size == 3)
|
assertEquals(replayedMap.size, 3)
|
||||||
|
|
||||||
sourceList.removeAt(0)
|
sourceList.removeAt(0)
|
||||||
require(replayedMap.size == 2)
|
assertEquals(replayedMap.size, 2)
|
||||||
require(replayedMap[2] == 2)
|
assertEquals(replayedMap[2], 2)
|
||||||
require(replayedMap[1] == 4)
|
assertEquals(replayedMap[1], 4)
|
||||||
|
|
||||||
sourceList.add(1, 12)
|
sourceList.add(1, 12)
|
||||||
require(replayedMap.size == 3)
|
assertEquals(replayedMap.size, 3)
|
||||||
require(replayedMap[2] == 2)
|
assertEquals(replayedMap[2], 2)
|
||||||
require(replayedMap[1] == 4)
|
assertEquals(replayedMap[1], 4)
|
||||||
require(replayedMap[0] == 12)
|
assertEquals(replayedMap[0], 12)
|
||||||
|
|
||||||
sourceList.clear()
|
sourceList.clear()
|
||||||
require(replayedMap.size == 0)
|
assertEquals(replayedMap.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun updateWorks() {
|
fun updateWorks() {
|
||||||
sourceList.addAll(2, 4)
|
sourceList.addAll(2, 4)
|
||||||
require(replayedMap.size == 3)
|
assertEquals(replayedMap.size, 3)
|
||||||
|
|
||||||
sourceList[1] = 5
|
sourceList[1] = 5
|
||||||
require(replayedMap.size == 3)
|
assertEquals(replayedMap.size, 3)
|
||||||
require(replayedMap[0] == 0)
|
assertEquals(replayedMap[0], 0)
|
||||||
require(replayedMap[2] == 5)
|
assertEquals(replayedMap[2], 5)
|
||||||
require(replayedMap[1] == 4)
|
assertEquals(replayedMap[1], 4)
|
||||||
|
|
||||||
sourceList.removeAt(1)
|
sourceList.removeAt(1)
|
||||||
require(replayedMap.size == 2)
|
assertEquals(replayedMap.size, 2)
|
||||||
require(replayedMap[0] == 0)
|
assertEquals(replayedMap[0], 0)
|
||||||
require(replayedMap[1] == 4)
|
assertEquals(replayedMap[1], 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@ import javafx.collections.ObservableList
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ConcatenatedListTest {
|
class ConcatenatedListTest {
|
||||||
|
|
||||||
var sourceList = FXCollections.observableArrayList<ObservableList<String>>(FXCollections.observableArrayList("hello"))
|
lateinit var sourceList: ObservableList<ObservableList<String>>
|
||||||
var concatenatedList = ConcatenatedList(sourceList)
|
lateinit var concatenatedList: ConcatenatedList<String>
|
||||||
var replayedList = ReplayedList(concatenatedList)
|
lateinit var replayedList: ObservableList<String>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@ -19,42 +20,70 @@ class ConcatenatedListTest {
|
|||||||
replayedList = ReplayedList(concatenatedList)
|
replayedList = ReplayedList(concatenatedList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A helper function for tests that checks internal invariants.
|
||||||
|
fun <A> ConcatenatedList<A>.checkInvariants() {
|
||||||
|
assertEquals(nestedIndexOffsets.size, source.size)
|
||||||
|
var currentOffset = 0
|
||||||
|
for (i in 0 .. source.size - 1) {
|
||||||
|
currentOffset += source[i].size
|
||||||
|
assertEquals(nestedIndexOffsets[i], currentOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(indexMap.size, source.size)
|
||||||
|
for (entry in indexMap) {
|
||||||
|
val (wrapped, pair) = entry
|
||||||
|
val index = pair.first
|
||||||
|
val foundListIndices = ArrayList<Int>()
|
||||||
|
source.forEachIndexed { i, list ->
|
||||||
|
if (wrapped.observableList == list) {
|
||||||
|
foundListIndices.add(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require(foundListIndices.any { it == index })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedList.size == 1)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 1)
|
||||||
|
assertEquals(replayedList[0], "hello")
|
||||||
|
|
||||||
sourceList.add(FXCollections.observableArrayList("a", "b"))
|
sourceList.add(FXCollections.observableArrayList("a", "b"))
|
||||||
require(replayedList.size == 3)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[1] == "a")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "b")
|
assertEquals(replayedList[1], "a")
|
||||||
|
assertEquals(replayedList[2], "b")
|
||||||
|
|
||||||
sourceList.add(1, FXCollections.observableArrayList("c"))
|
sourceList.add(1, FXCollections.observableArrayList("c"))
|
||||||
require(replayedList.size == 4)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 4)
|
||||||
require(replayedList[1] == "c")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "a")
|
assertEquals(replayedList[1], "c")
|
||||||
require(replayedList[3] == "b")
|
assertEquals(replayedList[2], "a")
|
||||||
|
assertEquals(replayedList[3], "b")
|
||||||
|
|
||||||
sourceList[0].addAll("d", "e")
|
sourceList[0].addAll("d", "e")
|
||||||
require(replayedList.size == 6)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 6)
|
||||||
require(replayedList[1] == "d")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "e")
|
assertEquals(replayedList[1], "d")
|
||||||
require(replayedList[3] == "c")
|
assertEquals(replayedList[2], "e")
|
||||||
require(replayedList[4] == "a")
|
assertEquals(replayedList[3], "c")
|
||||||
require(replayedList[5] == "b")
|
assertEquals(replayedList[4], "a")
|
||||||
|
assertEquals(replayedList[5], "b")
|
||||||
|
|
||||||
sourceList[1].add(0, "f")
|
sourceList[1].add(0, "f")
|
||||||
require(replayedList.size == 7)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 7)
|
||||||
require(replayedList[1] == "d")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "e")
|
assertEquals(replayedList[1], "d")
|
||||||
require(replayedList[3] == "f")
|
assertEquals(replayedList[2], "e")
|
||||||
require(replayedList[4] == "c")
|
assertEquals(replayedList[3], "f")
|
||||||
require(replayedList[5] == "a")
|
assertEquals(replayedList[4], "c")
|
||||||
require(replayedList[6] == "b")
|
assertEquals(replayedList[5], "a")
|
||||||
|
assertEquals(replayedList[6], "b")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -65,38 +94,47 @@ class ConcatenatedListTest {
|
|||||||
sourceList[1].add(0, "f")
|
sourceList[1].add(0, "f")
|
||||||
|
|
||||||
sourceList.removeAt(1)
|
sourceList.removeAt(1)
|
||||||
require(replayedList.size == 5)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 5)
|
||||||
require(replayedList[1] == "d")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "e")
|
assertEquals(replayedList[1], "d")
|
||||||
require(replayedList[3] == "a")
|
assertEquals(replayedList[2], "e")
|
||||||
require(replayedList[4] == "b")
|
assertEquals(replayedList[3], "a")
|
||||||
|
assertEquals(replayedList[4], "b")
|
||||||
|
|
||||||
sourceList[0].clear()
|
sourceList[0].clear()
|
||||||
require(replayedList.size == 2)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "a")
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[1] == "b")
|
assertEquals(replayedList[0], "a")
|
||||||
|
assertEquals(replayedList[1], "b")
|
||||||
|
|
||||||
|
sourceList[1].removeAt(0)
|
||||||
|
concatenatedList.checkInvariants()
|
||||||
|
assertEquals(replayedList.size, 1)
|
||||||
|
assertEquals(replayedList[0], "b")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun permutationWorks() {
|
fun permutationWorks() {
|
||||||
sourceList.addAll(FXCollections.observableArrayList("a", "b"), FXCollections.observableArrayList("c"))
|
sourceList.addAll(FXCollections.observableArrayList("a", "b"), FXCollections.observableArrayList("c"))
|
||||||
require(replayedList.size == 4)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 4)
|
||||||
require(replayedList[1] == "a")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "b")
|
assertEquals(replayedList[1], "a")
|
||||||
require(replayedList[3] == "c")
|
assertEquals(replayedList[2], "b")
|
||||||
|
assertEquals(replayedList[3], "c")
|
||||||
|
|
||||||
sourceList.sortWith(object : Comparator<ObservableList<String>> {
|
sourceList.sortWith(object : Comparator<ObservableList<String>> {
|
||||||
override fun compare(p0: ObservableList<String>, p1: ObservableList<String>): Int {
|
override fun compare(p0: ObservableList<String>, p1: ObservableList<String>): Int {
|
||||||
return p0.size - p1.size
|
return p0.size - p1.size
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
require(replayedList.size == 4)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 4)
|
||||||
require(replayedList[1] == "c")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "a")
|
assertEquals(replayedList[1], "c")
|
||||||
require(replayedList[3] == "b")
|
assertEquals(replayedList[2], "a")
|
||||||
|
assertEquals(replayedList[3], "b")
|
||||||
|
|
||||||
sourceList.add(0, FXCollections.observableArrayList("d", "e", "f"))
|
sourceList.add(0, FXCollections.observableArrayList("d", "e", "f"))
|
||||||
sourceList.sortWith(object : Comparator<ObservableList<String>> {
|
sourceList.sortWith(object : Comparator<ObservableList<String>> {
|
||||||
@ -104,14 +142,15 @@ class ConcatenatedListTest {
|
|||||||
return p0.size - p1.size
|
return p0.size - p1.size
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
require(replayedList.size == 7)
|
concatenatedList.checkInvariants()
|
||||||
require(replayedList[0] == "hello")
|
assertEquals(replayedList.size, 7)
|
||||||
require(replayedList[1] == "c")
|
assertEquals(replayedList[0], "hello")
|
||||||
require(replayedList[2] == "a")
|
assertEquals(replayedList[1], "c")
|
||||||
require(replayedList[3] == "b")
|
assertEquals(replayedList[2], "a")
|
||||||
require(replayedList[4] == "d")
|
assertEquals(replayedList[3], "b")
|
||||||
require(replayedList[5] == "e")
|
assertEquals(replayedList[4], "d")
|
||||||
require(replayedList[6] == "f")
|
assertEquals(replayedList[5], "e")
|
||||||
|
assertEquals(replayedList[6], "f")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,16 @@ package com.r3corda.client.fxutils
|
|||||||
|
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
|
import javafx.collections.ObservableList
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class FlattenedListTest {
|
class FlattenedListTest {
|
||||||
|
|
||||||
var sourceList = FXCollections.observableArrayList(SimpleObjectProperty(1234))
|
lateinit var sourceList: ObservableList<SimpleObjectProperty<Int>>
|
||||||
var flattenedList = FlattenedList(sourceList)
|
lateinit var flattenedList: ObservableList<Int>
|
||||||
var replayedList = ReplayedList(flattenedList)
|
lateinit var replayedList: ObservableList<Int>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@ -20,73 +22,73 @@ class FlattenedListTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
|
|
||||||
sourceList.add(SimpleObjectProperty(12))
|
sourceList.add(SimpleObjectProperty(12))
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 12)
|
assertEquals(replayedList[1], 12)
|
||||||
|
|
||||||
sourceList.add(SimpleObjectProperty(34))
|
sourceList.add(SimpleObjectProperty(34))
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 12)
|
assertEquals(replayedList[1], 12)
|
||||||
require(replayedList[2] == 34)
|
assertEquals(replayedList[2], 34)
|
||||||
|
|
||||||
sourceList.add(0, SimpleObjectProperty(56))
|
sourceList.add(0, SimpleObjectProperty(56))
|
||||||
require(replayedList.size == 4)
|
assertEquals(replayedList.size, 4)
|
||||||
require(replayedList[0] == 56)
|
assertEquals(replayedList[0], 56)
|
||||||
require(replayedList[1] == 1234)
|
assertEquals(replayedList[1], 1234)
|
||||||
require(replayedList[2] == 12)
|
assertEquals(replayedList[2], 12)
|
||||||
require(replayedList[3] == 34)
|
assertEquals(replayedList[3], 34)
|
||||||
|
|
||||||
sourceList.addAll(2, listOf(SimpleObjectProperty(78), SimpleObjectProperty(910)))
|
sourceList.addAll(2, listOf(SimpleObjectProperty(78), SimpleObjectProperty(910)))
|
||||||
require(replayedList.size == 6)
|
assertEquals(replayedList.size, 6)
|
||||||
require(replayedList[0] == 56)
|
assertEquals(replayedList[0], 56)
|
||||||
require(replayedList[1] == 1234)
|
assertEquals(replayedList[1], 1234)
|
||||||
require(replayedList[2] == 78)
|
assertEquals(replayedList[2], 78)
|
||||||
require(replayedList[3] == 910)
|
assertEquals(replayedList[3], 910)
|
||||||
require(replayedList[4] == 12)
|
assertEquals(replayedList[4], 12)
|
||||||
require(replayedList[5] == 34)
|
assertEquals(replayedList[5], 34)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeWorks() {
|
fun removeWorks() {
|
||||||
val firstRemoved = sourceList.removeAt(0)
|
val firstRemoved = sourceList.removeAt(0)
|
||||||
require(firstRemoved.get() == 1234)
|
assertEquals(firstRemoved.get(), 1234)
|
||||||
require(replayedList.size == 0)
|
assertEquals(replayedList.size, 0)
|
||||||
firstRemoved.set(123)
|
firstRemoved.set(123)
|
||||||
|
|
||||||
sourceList.add(SimpleObjectProperty(12))
|
sourceList.add(SimpleObjectProperty(12))
|
||||||
sourceList.add(SimpleObjectProperty(34))
|
sourceList.add(SimpleObjectProperty(34))
|
||||||
sourceList.add(SimpleObjectProperty(56))
|
sourceList.add(SimpleObjectProperty(56))
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
val secondRemoved = sourceList.removeAt(1)
|
val secondRemoved = sourceList.removeAt(1)
|
||||||
require(secondRemoved.get() == 34)
|
assertEquals(secondRemoved.get(), 34)
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 12)
|
assertEquals(replayedList[0], 12)
|
||||||
require(replayedList[1] == 56)
|
assertEquals(replayedList[1], 56)
|
||||||
secondRemoved.set(123)
|
secondRemoved.set(123)
|
||||||
|
|
||||||
sourceList.clear()
|
sourceList.clear()
|
||||||
require(replayedList.size == 0)
|
assertEquals(replayedList.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun updatingObservableWorks() {
|
fun updatingObservableWorks() {
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
sourceList[0].set(4321)
|
sourceList[0].set(4321)
|
||||||
require(replayedList[0] == 4321)
|
assertEquals(replayedList[0], 4321)
|
||||||
|
|
||||||
sourceList.add(0, SimpleObjectProperty(12))
|
sourceList.add(0, SimpleObjectProperty(12))
|
||||||
sourceList[1].set(8765)
|
sourceList[1].set(8765)
|
||||||
require(replayedList[0] == 12)
|
assertEquals(replayedList[0], 12)
|
||||||
require(replayedList[1] == 8765)
|
assertEquals(replayedList[1], 8765)
|
||||||
|
|
||||||
sourceList[0].set(34)
|
sourceList[0].set(34)
|
||||||
require(replayedList[0] == 34)
|
assertEquals(replayedList[0], 34)
|
||||||
require(replayedList[1] == 8765)
|
assertEquals(replayedList[1], 8765)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -94,20 +96,20 @@ class FlattenedListTest {
|
|||||||
val observable = SimpleObjectProperty(12)
|
val observable = SimpleObjectProperty(12)
|
||||||
sourceList.add(observable)
|
sourceList.add(observable)
|
||||||
sourceList.add(observable)
|
sourceList.add(observable)
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 12)
|
assertEquals(replayedList[1], 12)
|
||||||
require(replayedList[2] == 12)
|
assertEquals(replayedList[2], 12)
|
||||||
|
|
||||||
observable.set(34)
|
observable.set(34)
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 34)
|
assertEquals(replayedList[1], 34)
|
||||||
require(replayedList[2] == 34)
|
assertEquals(replayedList[2], 34)
|
||||||
|
|
||||||
sourceList.removeAt(1)
|
sourceList.removeAt(1)
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 34)
|
assertEquals(replayedList[1], 34)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,71 +5,72 @@ import javafx.collections.ObservableList
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class LeftOuterJoinedMapTest {
|
class LeftOuterJoinedMapTest {
|
||||||
|
|
||||||
data class Person(val name: String, val age: Int)
|
data class Person(val name: String, val age: Int)
|
||||||
data class Dog(val name: String, val owner: String)
|
data class Dog(val name: String, val owner: String)
|
||||||
|
|
||||||
var people = FXCollections.observableArrayList<Person>(Person("Alice", 12))
|
lateinit var people: ObservableList<Person>
|
||||||
var dogs = FXCollections.observableArrayList<Dog>(Dog("Scruffy", owner = "Bob"))
|
lateinit var dogs: ObservableList<Dog>
|
||||||
var joinedList = people.leftOuterJoin(dogs, Person::name, Dog::owner)
|
lateinit var joinedList: ObservableList<Pair<Person, ObservableList<Dog>>>
|
||||||
// We replay the nested observable as well
|
lateinit var replayedList: ObservableList<out Pair<Person, ObservableList<Dog>>>
|
||||||
var replayedList = ReplayedList(joinedList.map { Pair(it.first, ReplayedList(it.second)) })
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
people = FXCollections.observableArrayList<Person>(Person("Alice", 12))
|
people = FXCollections.observableArrayList<Person>(Person("Alice", 12))
|
||||||
dogs = FXCollections.observableArrayList<Dog>(Dog("Scruffy", owner = "Bob"))
|
dogs = FXCollections.observableArrayList<Dog>(Dog("Scruffy", owner = "Bob"))
|
||||||
joinedList = people.leftOuterJoin(dogs, Person::name, Dog::owner)
|
joinedList = people.leftOuterJoin(dogs, Person::name, Dog::owner) { person, dogs -> Pair(person, dogs) }
|
||||||
|
// We replay the nested observable as well
|
||||||
replayedList = ReplayedList(joinedList.map { Pair(it.first, ReplayedList(it.second)) })
|
replayedList = ReplayedList(joinedList.map { Pair(it.first, ReplayedList(it.second)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO perhaps these are too brittle because they test indices that are not stable. Use Expect dsl?
|
// TODO perhaps these are too brittle because they test indices that are not stable. Use Expect dsl?
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 0)
|
assertEquals(replayedList[0].second.size, 0)
|
||||||
|
|
||||||
dogs.add(Dog("Scooby", owner = "Alice"))
|
dogs.add(Dog("Scooby", owner = "Alice"))
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 1)
|
assertEquals(replayedList[0].second.size, 1)
|
||||||
require(replayedList[0].second[0].name == "Scooby")
|
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||||
|
|
||||||
people.add(Person("Bob", 34))
|
people.add(Person("Bob", 34))
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 1)
|
assertEquals(replayedList[0].second.size, 1)
|
||||||
require(replayedList[0].second[0].name == "Scooby")
|
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||||
require(replayedList[1].first.name == "Bob")
|
assertEquals(replayedList[1].first.name, "Bob")
|
||||||
require(replayedList[1].second.size == 1)
|
assertEquals(replayedList[1].second.size, 1)
|
||||||
require(replayedList[1].second[0].name == "Scruffy")
|
assertEquals(replayedList[1].second[0].name, "Scruffy")
|
||||||
|
|
||||||
dogs.add(Dog("Bella", owner = "Bob"))
|
dogs.add(Dog("Bella", owner = "Bob"))
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 1)
|
assertEquals(replayedList[0].second.size, 1)
|
||||||
require(replayedList[0].second[0].name == "Scooby")
|
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||||
require(replayedList[1].first.name == "Bob")
|
assertEquals(replayedList[1].first.name, "Bob")
|
||||||
require(replayedList[1].second.size == 2)
|
assertEquals(replayedList[1].second.size, 2)
|
||||||
require(replayedList[1].second[0].name == "Bella")
|
assertEquals(replayedList[1].second[0].name, "Bella")
|
||||||
require(replayedList[1].second[1].name == "Scruffy")
|
assertEquals(replayedList[1].second[1].name, "Scruffy")
|
||||||
|
|
||||||
// We have another Alice wat
|
// We have another Alice wat
|
||||||
people.add(Person("Alice", 91))
|
people.add(Person("Alice", 91))
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 1)
|
assertEquals(replayedList[0].second.size, 1)
|
||||||
require(replayedList[0].second[0].name == "Scooby")
|
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||||
require(replayedList[1].first.name == "Alice")
|
assertEquals(replayedList[1].first.name, "Alice")
|
||||||
require(replayedList[1].second.size == 1)
|
assertEquals(replayedList[1].second.size, 1)
|
||||||
require(replayedList[1].second[0].name == "Scooby")
|
assertEquals(replayedList[1].second[0].name, "Scooby")
|
||||||
require(replayedList[2].first.name == "Bob")
|
assertEquals(replayedList[2].first.name, "Bob")
|
||||||
require(replayedList[2].second.size == 2)
|
assertEquals(replayedList[2].second.size, 2)
|
||||||
require(replayedList[2].second[0].name == "Bella")
|
assertEquals(replayedList[2].second[0].name, "Bella")
|
||||||
require(replayedList[2].second[1].name == "Scruffy")
|
assertEquals(replayedList[2].second[1].name, "Scruffy")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,37 +80,37 @@ class LeftOuterJoinedMapTest {
|
|||||||
people.add(Person("Bob", 34))
|
people.add(Person("Bob", 34))
|
||||||
dogs.add(Dog("Bella", owner = "Bob"))
|
dogs.add(Dog("Bella", owner = "Bob"))
|
||||||
|
|
||||||
require(people.removeAt(0).name == "Alice")
|
assertEquals(people.removeAt(0).name, "Alice")
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0].first.name == "Bob")
|
assertEquals(replayedList[0].first.name, "Bob")
|
||||||
require(replayedList[0].second.size == 2)
|
assertEquals(replayedList[0].second.size, 2)
|
||||||
require(replayedList[0].second[0].name == "Bella")
|
assertEquals(replayedList[0].second[0].name, "Bella")
|
||||||
require(replayedList[0].second[1].name == "Scruffy")
|
assertEquals(replayedList[0].second[1].name, "Scruffy")
|
||||||
|
|
||||||
require(dogs.removeAt(0).name == "Scruffy")
|
assertEquals(dogs.removeAt(0).name, "Scruffy")
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0].first.name == "Bob")
|
assertEquals(replayedList[0].first.name, "Bob")
|
||||||
require(replayedList[0].second.size == 1)
|
assertEquals(replayedList[0].second.size, 1)
|
||||||
require(replayedList[0].second[0].name == "Bella")
|
assertEquals(replayedList[0].second[0].name, "Bella")
|
||||||
|
|
||||||
people.add(Person("Alice", 213))
|
people.add(Person("Alice", 213))
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 1)
|
assertEquals(replayedList[0].second.size, 1)
|
||||||
require(replayedList[0].second[0].name == "Scooby")
|
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||||
require(replayedList[1].first.name == "Bob")
|
assertEquals(replayedList[1].first.name, "Bob")
|
||||||
require(replayedList[1].second.size == 1)
|
assertEquals(replayedList[1].second.size, 1)
|
||||||
require(replayedList[1].second[0].name == "Bella")
|
assertEquals(replayedList[1].second[0].name, "Bella")
|
||||||
|
|
||||||
dogs.clear()
|
dogs.clear()
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0].first.name == "Alice")
|
assertEquals(replayedList[0].first.name, "Alice")
|
||||||
require(replayedList[0].second.size == 0)
|
assertEquals(replayedList[0].second.size, 0)
|
||||||
require(replayedList[1].first.name == "Bob")
|
assertEquals(replayedList[1].first.name, "Bob")
|
||||||
require(replayedList[1].second.size == 0)
|
assertEquals(replayedList[1].second.size, 0)
|
||||||
|
|
||||||
people.clear()
|
people.clear()
|
||||||
require(replayedList.size == 0)
|
assertEquals(replayedList.size, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package com.r3corda.client.fxutils
|
|
||||||
|
|
||||||
import org.junit.Before
|
|
||||||
|
|
||||||
class MapValuesListTest {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +1,16 @@
|
|||||||
package com.r3corda.client.fxutils
|
package com.r3corda.client.fxutils
|
||||||
|
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
|
import javafx.collections.ObservableList
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class MappedListTest {
|
class MappedListTest {
|
||||||
|
|
||||||
var sourceList = FXCollections.observableArrayList("Alice")
|
lateinit var sourceList: ObservableList<String>
|
||||||
var mappedList = MappedList(sourceList) { it.length }
|
lateinit var mappedList: ObservableList<Int>
|
||||||
var replayedList = ReplayedList(mappedList)
|
lateinit var replayedList: ObservableList<Int>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
@ -19,19 +21,19 @@ class MappedListTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0] == 5)
|
assertEquals(replayedList[0], 5)
|
||||||
|
|
||||||
sourceList.add("Bob")
|
sourceList.add("Bob")
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 5)
|
assertEquals(replayedList[0], 5)
|
||||||
require(replayedList[1] == 3)
|
assertEquals(replayedList[1], 3)
|
||||||
|
|
||||||
sourceList.add(0, "Charlie")
|
sourceList.add(0, "Charlie")
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0] == 7)
|
assertEquals(replayedList[0], 7)
|
||||||
require(replayedList[1] == 5)
|
assertEquals(replayedList[1], 5)
|
||||||
require(replayedList[2] == 3)
|
assertEquals(replayedList[2], 3)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,12 +41,12 @@ class MappedListTest {
|
|||||||
fun removeWorks() {
|
fun removeWorks() {
|
||||||
sourceList.add("Bob")
|
sourceList.add("Bob")
|
||||||
sourceList.add(0, "Charlie")
|
sourceList.add(0, "Charlie")
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
|
|
||||||
sourceList.removeAt(1)
|
sourceList.removeAt(1)
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 7)
|
assertEquals(replayedList[0], 7)
|
||||||
require(replayedList[1] == 3)
|
assertEquals(replayedList[1], 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -54,13 +56,13 @@ class MappedListTest {
|
|||||||
|
|
||||||
sourceList.sortBy { it.length }
|
sourceList.sortBy { it.length }
|
||||||
|
|
||||||
require(sourceList[0] == "Bob")
|
assertEquals(sourceList[0], "Bob")
|
||||||
require(sourceList[1] == "Alice")
|
assertEquals(sourceList[1], "Alice")
|
||||||
require(sourceList[2] == "Charlie")
|
assertEquals(sourceList[2], "Charlie")
|
||||||
|
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0] == 3)
|
assertEquals(replayedList[0], 3)
|
||||||
require(replayedList[1] == 5)
|
assertEquals(replayedList[1], 5)
|
||||||
require(replayedList[2] == 7)
|
assertEquals(replayedList[2], 7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.r3corda.client.fxutils
|
|||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ReplayedListTest {
|
class ReplayedListTest {
|
||||||
|
|
||||||
@ -17,70 +18,70 @@ class ReplayedListTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addWorks() {
|
fun addWorks() {
|
||||||
require(replayedList.size == 1)
|
assertEquals(replayedList.size, 1)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
|
|
||||||
sourceList.add(12)
|
sourceList.add(12)
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 12)
|
assertEquals(replayedList[1], 12)
|
||||||
|
|
||||||
sourceList.add(34)
|
sourceList.add(34)
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
require(replayedList[1] == 12)
|
assertEquals(replayedList[1], 12)
|
||||||
require(replayedList[2] == 34)
|
assertEquals(replayedList[2], 34)
|
||||||
|
|
||||||
sourceList.add(0, 56)
|
sourceList.add(0, 56)
|
||||||
require(replayedList.size == 4)
|
assertEquals(replayedList.size, 4)
|
||||||
require(replayedList[0] == 56)
|
assertEquals(replayedList[0], 56)
|
||||||
require(replayedList[1] == 1234)
|
assertEquals(replayedList[1], 1234)
|
||||||
require(replayedList[2] == 12)
|
assertEquals(replayedList[2], 12)
|
||||||
require(replayedList[3] == 34)
|
assertEquals(replayedList[3], 34)
|
||||||
|
|
||||||
sourceList.addAll(2, listOf(78, 910))
|
sourceList.addAll(2, listOf(78, 910))
|
||||||
require(replayedList.size == 6)
|
assertEquals(replayedList.size, 6)
|
||||||
require(replayedList[0] == 56)
|
assertEquals(replayedList[0], 56)
|
||||||
require(replayedList[1] == 1234)
|
assertEquals(replayedList[1], 1234)
|
||||||
require(replayedList[2] == 78)
|
assertEquals(replayedList[2], 78)
|
||||||
require(replayedList[3] == 910)
|
assertEquals(replayedList[3], 910)
|
||||||
require(replayedList[4] == 12)
|
assertEquals(replayedList[4], 12)
|
||||||
require(replayedList[5] == 34)
|
assertEquals(replayedList[5], 34)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeWorks() {
|
fun removeWorks() {
|
||||||
val firstRemoved = sourceList.removeAt(0)
|
val firstRemoved = sourceList.removeAt(0)
|
||||||
require(firstRemoved == 1234)
|
assertEquals(firstRemoved, 1234)
|
||||||
require(replayedList.size == 0)
|
assertEquals(replayedList.size, 0)
|
||||||
|
|
||||||
sourceList.add(12)
|
sourceList.add(12)
|
||||||
sourceList.add(34)
|
sourceList.add(34)
|
||||||
sourceList.add(56)
|
sourceList.add(56)
|
||||||
require(replayedList.size == 3)
|
assertEquals(replayedList.size, 3)
|
||||||
val secondRemoved = sourceList.removeAt(1)
|
val secondRemoved = sourceList.removeAt(1)
|
||||||
require(secondRemoved == 34)
|
assertEquals(secondRemoved, 34)
|
||||||
require(replayedList.size == 2)
|
assertEquals(replayedList.size, 2)
|
||||||
require(replayedList[0] == 12)
|
assertEquals(replayedList[0], 12)
|
||||||
require(replayedList[1] == 56)
|
assertEquals(replayedList[1], 56)
|
||||||
|
|
||||||
sourceList.clear()
|
sourceList.clear()
|
||||||
require(replayedList.size == 0)
|
assertEquals(replayedList.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun updateWorks() {
|
fun updateWorks() {
|
||||||
require(replayedList[0] == 1234)
|
assertEquals(replayedList[0], 1234)
|
||||||
sourceList[0] = 4321
|
sourceList[0] = 4321
|
||||||
require(replayedList[0] == 4321)
|
assertEquals(replayedList[0], 4321)
|
||||||
|
|
||||||
sourceList.add(0, 12)
|
sourceList.add(0, 12)
|
||||||
sourceList[1] = 8765
|
sourceList[1] = 8765
|
||||||
require(replayedList[0] == 12)
|
assertEquals(replayedList[0], 12)
|
||||||
require(replayedList[1] == 8765)
|
assertEquals(replayedList[1], 8765)
|
||||||
|
|
||||||
sourceList[0] = 34
|
sourceList[0] = 34
|
||||||
require(replayedList[0] == 34)
|
assertEquals(replayedList[0], 34)
|
||||||
require(replayedList[1] == 8765)
|
assertEquals(replayedList[1], 8765)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,11 @@ package com.r3corda.client.fxutils
|
|||||||
|
|
||||||
import javafx.collections.MapChangeListener
|
import javafx.collections.MapChangeListener
|
||||||
import javafx.collections.ObservableMap
|
import javafx.collections.ObservableMap
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ReplayedMap] simply replays changes done to the source map. Used for testing changes.
|
||||||
|
*/
|
||||||
class ReplayedMap<K, A>(sourceMap: ObservableMap<K, A>) : ReadOnlyBackedObservableMapBase<K, A, Unit>() {
|
class ReplayedMap<K, A>(sourceMap: ObservableMap<K, A>) : ReadOnlyBackedObservableMapBase<K, A, Unit>() {
|
||||||
init {
|
init {
|
||||||
sourceMap.forEach {
|
sourceMap.forEach {
|
||||||
@ -10,7 +14,7 @@ class ReplayedMap<K, A>(sourceMap: ObservableMap<K, A>) : ReadOnlyBackedObservab
|
|||||||
}
|
}
|
||||||
sourceMap.addListener { change: MapChangeListener.Change<out K, out A> ->
|
sourceMap.addListener { change: MapChangeListener.Change<out K, out A> ->
|
||||||
if (change.wasRemoved()) {
|
if (change.wasRemoved()) {
|
||||||
require(backingMap.remove(change.key)!!.first == change.valueRemoved)
|
assertEquals(backingMap.remove(change.key)!!.first, change.valueRemoved)
|
||||||
}
|
}
|
||||||
if (change.wasAdded()) {
|
if (change.wasAdded()) {
|
||||||
backingMap.set(change.key, Pair(change.valueAdded, Unit))
|
backingMap.set(change.key, Pair(change.valueAdded, Unit))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user