All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tornadofx.Collections.kt Maven / Gradle / Ivy

There is a newer version: 1.7.20
Show newest version
@file:Suppress("unused")

package tornadofx

import javafx.beans.Observable
import javafx.beans.WeakListener
import javafx.collections.*
import tornadofx.FX.IgnoreParentBuilder.No
import tornadofx.FX.IgnoreParentBuilder.Once
import java.lang.ref.WeakReference
import java.util.*

/**
 * Returns an empty new [ObservableIntegerArray].
 */
fun observableIntArrayOf(): ObservableIntegerArray = FXCollections.observableIntegerArray()

/**
 * Returns a new [ObservableIntegerArray] with the given [elements].
 */
fun observableIntArrayOf(vararg elements: Int): ObservableIntegerArray = FXCollections.observableIntegerArray(*elements)

/**
 * Returns an empty new [ObservableFloatArray].
 */
fun observableFloatArrayOf(): ObservableFloatArray = FXCollections.observableFloatArray()

/**
 * Returns a new [ObservableFloatArray] with the given [elements].
 */
fun observableFloatArrayOf(vararg elements: Float): ObservableFloatArray = FXCollections.observableFloatArray(*elements)


/**
 * Returns an empty new [ObservableList].
 */
fun  observableListOf(): ObservableList = FXCollections.observableArrayList()

/**
 * Returns a new [ObservableList] with the given [elements].
 */
fun  observableListOf(vararg elements: T): ObservableList = FXCollections.observableArrayList(*elements)

/**
 * Returns a new [ObservableList] containing all elements from the given [collection].
 */
fun  observableListOf(collection: Collection): ObservableList = FXCollections.observableArrayList(collection)

/**
 * Returns an empty new [ObservableList] with the given [extractor]. This list reports element updates.
 */
fun  observableListOf(extractor: (T)->Array): ObservableList = FXCollections.observableArrayList(extractor)

/**
 * Returns an empty new [ObservableSet]
 */
fun  observableSetOf(): ObservableSet = FXCollections.observableSet()

/**
 * Returns a new [ObservableSet] with the given elements.
 */
fun  observableSetOf(vararg elements: T): ObservableSet = FXCollections.observableSet(*elements)

/**
 * Returns an empty new [ObservableMap]
 */
fun  observableMapOf(): ObservableMap = FXCollections.observableHashMap()

/**
 * Returns a new [ObservableMap] with the specified contents, given as a list of pairs
 * where the first component is the key and the second is the value.
 */
fun  observableMapOf(vararg pairs: Pair): ObservableMap = FXCollections.observableMap(pairs.toMap(hashMapOf()))


/**
 * Returns a new [ObservableIntegerArray] with the elements from the original array.
 */
fun IntArray.toObservable(): ObservableIntegerArray = FXCollections.observableIntegerArray(*this)

/**
 * Returns a new [ObservableIntegerArray] with the elements from the original array.
 */
fun Array.toObservable(): ObservableIntegerArray = FXCollections.observableIntegerArray(*this.toIntArray())

/**
 * Returns a new [ObservableFloatArray] with the elements from the original array.
 */
fun FloatArray.toObservable(): ObservableFloatArray = FXCollections.observableFloatArray(*this)

/**
 * Returns a new [ObservableFloatArray] with the elements from the original array.
 */
fun Array.toObservable(): ObservableFloatArray = FXCollections.observableFloatArray(*this.toFloatArray())


/**
 * Returns a new [ObservableList] with the elements from the original list.
 */
fun  List.toObservable(): ObservableList = FXCollections.observableList(toMutableList())

/**
 * Returns a new [ObservableSet] with the elements from the original set.
 */
fun  Set.toObservable(): ObservableSet = FXCollections.observableSet(toMutableSet())

/**
 * Returns a new [ObservableMap] with the elements from the original map.
 */
fun  Map.toObservable(): ObservableMap = FXCollections.observableMap(toMutableMap())


/**
 * Returns a new [ObservableList] that is backed by the original list.
 *
 * **Note:** If the original list is read-only, attempting to modify the returned list will result in an [UnsupportedOperationException]
 */
fun  List.asObservable(): ObservableList = FXCollections.observableList(this)

/**
 * Returns a new [ObservableSet] that is backed by the original set.
 *
 * **Note:** If the original set is read-only, attempting to modify the returned set will result in an [UnsupportedOperationException]
 */
fun  Set.asObservable(): ObservableSet = FXCollections.observableSet(this)

/**
 * Returns a new [ObservableMap] that is backed by the original map.
 *
 * **Note:** If the original map is read-only, attempting to modify the returned map will result in an [UnsupportedOperationException]
 */
fun  Map.asObservable(): ObservableMap = FXCollections.observableMap(this)


/**
 * Returns an unmodifiable [ObservableList] that wraps the original list.
 */
fun  ObservableList.asUnmodifiable(): ObservableList = FXCollections.unmodifiableObservableList(this)

/**
 * Returns an unmodifiable [ObservableSet] that wraps the original set.
 */
fun  ObservableSet.asUnmodifiable(): ObservableSet = FXCollections.unmodifiableObservableSet(this)

/**
 * Returns an unmodifiable [ObservableMap] that wraps the original map.
 */
fun  ObservableMap.asUnmodifiable(): ObservableMap = FXCollections.unmodifiableObservableMap(this)


/**
 * Fills the observable list with the provided [value].
 * Fires only **one** change notification on the list.
 */
fun  ObservableList.fill(value: T): Unit = FXCollections.fill(this, value)

/**
 * Reverse the order in the observable list.
 * Fires only **one** change notification on the list.
 */
fun  ObservableList.reverse(): Unit = FXCollections.reverse(this)

/**
 * Randomly shuffles elements in this observable list.
 * Fires only **one** change notification on the list.
 */
fun  ObservableList.shuffle(): Unit = FXCollections.shuffle(this)

/**
 * Randomly shuffles elements in this observable list using the specified [random] instance as the source of randomness.
 * Fires only **one** change notification on the list.
 */
fun  ObservableList.shuffle(random: Random): Unit = FXCollections.shuffle(this, random)

/**
 * Sorts elements in the observable list according to their natural sort order.
 * Fires only **one** change notification on the list.
 */
fun > ObservableList.sort() {
    if (size > 1) FXCollections.sort(this)
}

/**
 * Sorts elements in the observable list according to the order specified with [comparator].
 * Fires only **one** change notification on the list.
 */
fun  ObservableList.sortWith(comparator: Comparator) {
    if (size > 1) FXCollections.sort(this, comparator)
}

/**
 * Sorts elements in the observable list according to natural sort order of the value returned by specified [selector] function.
 * Fires only **one** change notification on the list.
 */
inline fun > ObservableList.sortBy(crossinline selector: (T) -> R?) {
    if (size > 1) sortWith(compareBy(selector))
}

/**
 * Sorts elements in the observable list descending according to natural sort order of the value returned by specified [selector] function.
 * Fires only **one** change notification on the list.
 */
inline fun > ObservableList.sortByDescending(crossinline selector: (T) -> R?) {
    if (size > 1) sortWith(compareByDescending(selector))
}


/**
 * Moves the given **T** item to the specified index
 */
fun  MutableList.move(item: T, newIndex: Int) {
    check(newIndex in 0 until size)
    val currentIndex = indexOf(item)
    if (currentIndex < 0) return
    removeAt(currentIndex)
    add(newIndex, item)
}

/**
 * Moves the given item at the `oldIndex` to the `newIndex`
 */
fun  MutableList.moveAt(oldIndex: Int, newIndex: Int) {
    check(oldIndex in 0 until size)
    check(newIndex in 0 until size)
    val item = this[oldIndex]
    removeAt(oldIndex)
    add(newIndex, item)
}

/**
 * Moves all items meeting a predicate to the given index
 */
fun  MutableList.moveAll(newIndex: Int, predicate: (T) -> Boolean) {
    check(newIndex in 0 until size)
    val split = partition(predicate)
    clear()
    addAll(split.second)
    addAll(if (newIndex >= size) size else newIndex, split.first)
}

/**
 * Moves the given element at specified index up the **MutableList** by one increment
 * unless it is at the top already which will result in no movement
 */
fun  MutableList.moveUpAt(index: Int) {
    if (index == 0) return
    check(index in indices, { "Invalid index $index for MutableList of size $size" })
    val newIndex = index - 1
    val item = this[index]
    removeAt(index)
    add(newIndex, item)
}

/**
 * Moves the given element **T** up the **MutableList** by one increment
 * unless it is at the bottom already which will result in no movement
 */
fun  MutableList.moveDownAt(index: Int) {
    if (index == size - 1) return
    check(index in indices, { "Invalid index $index for MutableList of size $size" })
    val newIndex = index + 1
    val item = this[index]
    removeAt(index)
    add(newIndex, item)
}

/**
 * Moves the given element **T** up the **MutableList** by an index increment
 * unless it is at the top already which will result in no movement.
 * Returns a `Boolean` indicating if move was successful
 */
fun  MutableList.moveUp(item: T): Boolean {
    val currentIndex = indexOf(item)
    if (currentIndex == -1) return false
    val newIndex = (currentIndex - 1)
    if (currentIndex <= 0) return false
    remove(item)
    add(newIndex, item)
    return true
}

/**
 * Moves the given element **T** up the **MutableList** by an index increment
 * unless it is at the bottom already which will result in no movement.
 * Returns a `Boolean` indicating if move was successful
 */
fun  MutableList.moveDown(item: T): Boolean {
    val currentIndex = indexOf(item)
    if (currentIndex == -1) return false
    val newIndex = (currentIndex + 1)
    if (newIndex >= size) return false
    remove(item)
    add(newIndex, item)
    return true
}


/**
 * Moves first element **T** up an index that satisfies the given **predicate**, unless its already at the top
 */
inline fun  MutableList.moveUp(crossinline predicate: (T) -> Boolean) = find(predicate)?.let { moveUp(it) }

/**
 * Moves first element **T** down an index that satisfies the given **predicate**, unless its already at the bottom
 */
inline fun  MutableList.moveDown(crossinline predicate: (T) -> Boolean) = find(predicate)?.let { moveDown(it) }

/**
 * Moves all **T** elements up an index that satisfy the given **predicate**, unless they are already at the top
 */
inline fun  MutableList.moveUpAll(crossinline predicate: (T) -> Boolean) = asSequence().withIndex()
        .filter { predicate.invoke(it.value) }
        .forEach { moveUpAt(it.index) }

/**
 * Moves all **T** elements down an index that satisfy the given **predicate**, unless they are already at the bottom
 */
inline fun  MutableList.moveDownAll(crossinline predicate: (T) -> Boolean) = asSequence().withIndex()
        .filter { predicate.invoke(it.value) }
        .forEach { moveDownAt(it.index) }


fun  MutableList.moveToTopWhere(predicate: (T) -> Boolean) {
    asSequence().filter(predicate).toList().asSequence().forEach {
        remove(it)
        add(0, it)
    }
}

fun  MutableList.moveToBottomWhere(predicate: (T) -> Boolean) {
    val end = size - 1
    asSequence().filter(predicate).toList().asSequence().forEach {
        remove(it)
        add(end, it)
    }
}


/**
 * Swaps the position of two items at two respective indices
 */
fun  MutableList.swap(indexOne: Int, indexTwo: Int) {
    if (this is ObservableList<*>) {
        if (indexOne == indexTwo) return
        val min = Math.min(indexOne, indexTwo)
        val max = Math.max(indexOne, indexTwo)
        val o2 = removeAt(max)
        val o1 = removeAt(min)
        add(min, o2)
        add(max, o1)
    } else {
        Collections.swap(this, indexOne, indexTwo)
    }
}

/**
 * Swaps the index position of two items
 */
fun  MutableList.swap(itemOne: T, itemTwo: T) = swap(indexOf(itemOne), indexOf(itemTwo))

/**
 * Bind this list to the given observable list by converting them into the correct type via the given converter.
 * Changes to the observable list are synced.
 */
fun  MutableList.bind(sourceList: ObservableList, converter: (SourceType) -> TargetType): ListConversionListener {
    val ignoringParentConverter: (SourceType) -> TargetType = {
        FX.ignoreParentBuilder = Once
        try {
            converter(it)
        } finally {
            FX.ignoreParentBuilder = No
        }
    }
    val listener = ListConversionListener(this, ignoringParentConverter)
    (this as?  ObservableList)?.setAll(sourceList.map(ignoringParentConverter)) ?: run {
        clear()
        addAll(sourceList.map(ignoringParentConverter))
    }
    sourceList.removeListener(listener)
    sourceList.addListener(listener)
    return listener
}

/**
 * Bind this list to the given observable list by converting them into the correct type via the given converter.
 * Changes to the observable list are synced.
 */
fun  MutableList.bind(sourceSet: ObservableSet, converter: (SourceType) -> TargetType): SetConversionListener {
    val ignoringParentConverter: (SourceType) -> TargetType = {
        FX.ignoreParentBuilder = Once
        try {
            converter(it)
        } finally {
            FX.ignoreParentBuilder = No
        }
    }
    val listener = SetConversionListener(this, ignoringParentConverter)
    if (this is ObservableList<*>) {
        sourceSet.forEach { source ->
            val converted = ignoringParentConverter(source)
            listener.sourceToTarget[source] = converted
        }
        (this as ObservableList).setAll(listener.sourceToTarget.values)
    } else {
        clear()
        addAll(sourceSet.map(ignoringParentConverter))
    }
    sourceSet.removeListener(listener)
    sourceSet.addListener(listener)
    return listener
}

fun  MutableList.bind(
        sourceMap: ObservableMap,
        converter: (SourceTypeKey, SourceTypeValue) -> TargetType
): MapConversionListener {
    val ignoringParentConverter: (SourceTypeKey, SourceTypeValue) -> TargetType = { key, value ->
        FX.ignoreParentBuilder = FX.IgnoreParentBuilder.Once
        try {
            converter(key, value)
        } finally {
            FX.ignoreParentBuilder = FX.IgnoreParentBuilder.No
        }
    }
    val listener = MapConversionListener(this, ignoringParentConverter)
    if (this is ObservableList<*>) {
        sourceMap.forEach { source ->
            val converted = ignoringParentConverter(source.key,source.value)
            listener.sourceToTarget[source] = converted
        }
        (this as ObservableList).setAll(listener.sourceToTarget.values)
    } else {
        clear()
        addAll(sourceMap.map{ignoringParentConverter(it.key, it.value) })
    }
    sourceMap.removeListener(listener)
    sourceMap.addListener(listener)
    return listener
}


/**
 * Listens to changes on a list of SourceType and keeps the target list in sync by converting
 * each object into the TargetType via the supplied converter.
 */
class ListConversionListener(targetList: MutableList, val converter: (SourceType) -> TargetType) : ListChangeListener, WeakListener {
    internal val targetRef: WeakReference> = WeakReference(targetList)

    override fun onChanged(change: ListChangeListener.Change) {
        val list = targetRef.get()
        if (list == null) {
            change.list.removeListener(this)
        } else {
            while (change.next()) {
                if (change.wasPermutated()) {
                    list.subList(change.from, change.to).clear()
                    list.addAll(change.from, change.list.subList(change.from, change.to).map(converter))
                } else {
                    if (change.wasRemoved()) {
                        list.subList(change.from, change.from + change.removedSize).clear()
                    }
                    if (change.wasAdded()) {
                        list.addAll(change.from, change.addedSubList.map(converter))
                    }
                }
            }
        }
    }

    override fun wasGarbageCollected() = targetRef.get() == null

    override fun hashCode() = targetRef.get().hashCode()

    override fun equals(other: Any?): Boolean {
        if (this === other) {
            return true
        }

        val ourList = targetRef.get() ?: return false

        if (other is ListConversionListener<*, *>) {
            val otherList = other.targetRef.get()
            return ourList === otherList
        }
        return false
    }
}

/**
 * Listens to changes on a Map of SourceTypeKey to SourceTypeValue and keeps the target list in sync by converting
 * each object into the TargetType via the supplied converter.
 */
class MapConversionListener(
        targetList: MutableList,
        val converter: (SourceTypeKey, SourceTypeValue) -> TargetType
) : MapChangeListener, WeakListener {

    internal val targetRef: WeakReference> = WeakReference(targetList)
    internal val sourceToTarget = HashMap, TargetType>()
    override fun onChanged(change: MapChangeListener.Change) {
        val list = targetRef.get()
        if (list == null) {
            change.map.removeListener(this)
        } else {
            if (change.wasRemoved()) {
                list.remove(converter(change.key, change.valueRemoved))
            }
            if (change.wasAdded()) {
                list.add(converter(change.key, change.valueAdded))
            }
        }
    }

    override fun wasGarbageCollected() = targetRef.get() == null

    override fun hashCode() = targetRef.get().hashCode()

    override fun equals(other: Any?): Boolean {
        if (this === other) {
            return true
        }

        val ourList = targetRef.get() ?: return false

        if (other is MapConversionListener<*, *, *>) {
            val otherList = other.targetRef.get()
            return ourList === otherList
        }
        return false
    }
}

/**
 * Listens to changes on a set of SourceType and keeps the target list in sync by converting
 * each object into the TargetType via the supplied converter.
 */
class SetConversionListener(targetList: MutableList, val converter: (SourceType) -> TargetType) : SetChangeListener, WeakListener {
    internal val targetRef: WeakReference> = WeakReference(targetList)
    internal val sourceToTarget = HashMap()

    override fun onChanged(change: SetChangeListener.Change) {
        val list = targetRef.get()
        if (list == null) {
            change.set.removeListener(this)
            sourceToTarget.clear()
        } else {
            if (change.wasRemoved()) {
                list.remove(sourceToTarget[change.elementRemoved])
                sourceToTarget.remove(change.elementRemoved)
            }
            if (change.wasAdded()) {
                val converted = converter(change.elementAdded)
                sourceToTarget[change.elementAdded] = converted
                list.add(converted)
            }
        }
    }

    override fun wasGarbageCollected() = targetRef.get() == null

    override fun hashCode() = targetRef.get().hashCode()

    override fun equals(other: Any?): Boolean {
        if (this === other) {
            return true
        }

        val ourList = targetRef.get() ?: return false

        if (other is SetConversionListener<*, *>) {
            val otherList = other.targetRef.get()
            return ourList === otherList
        }
        return false
    }
}

fun  ObservableList.invalidate() {
    if (isNotEmpty()) this[0] = this[0]
}

@Deprecated("Use `observableListOf()` instead.", ReplaceWith("observableListOf(entries)", "tornadofx.observableListOf"))
fun  observableList(vararg entries: T) : ObservableList = FXCollections.observableArrayList(entries.toList())




© 2015 - 2025 Weber Informatics LLC | Privacy Policy