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

ru.hnau.jutils.finisher.Finisher.kt Maven / Gradle / Ivy

The newest version!
package ru.hnau.jutils.finisher

import ru.hnau.jutils.TimeValue
import ru.hnau.jutils.helpers.weak.WeakListener
import ru.hnau.jutils.awaitOrElse
import ru.hnau.jutils.finisher.detacher.ActiveFinisherDetacher
import ru.hnau.jutils.finisher.detacher.FakeFinisherDetacher
import ru.hnau.jutils.finisher.detacher.FinisherDetacher
import ru.hnau.jutils.finisher.detacher.FinisherDetachers
import ru.hnau.jutils.helpers.Box
import ru.hnau.jutils.helpers.Completable
import ru.hnau.jutils.possible.Possible
import ru.hnau.jutils.producer.Producer
import ru.hnau.jutils.producer.detacher.ProducerDetachers
import ru.hnau.jutils.producer.locked_producer.FinishersLockedProducer
import java.util.concurrent.CancellationException
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.CountDownLatch

@Deprecated("Use kotlinx.coroutines.Deferred instead")
open class Finisher(
        action: (onFinished: (T) -> Unit) -> Unit
) : Completable {

    var result: Box? = null
        private set

    private val listeners = CopyOnWriteArraySet<(T) -> Unit>()

    val finished: Boolean
        get() = result != null

    init {
        action.invoke(::onResultReceived)
    }

    fun applyAwait(listener: (T) -> Unit) =
            apply { await(listener) }

    fun addToLocker(lockedProducer: FinishersLockedProducer) =
            apply(lockedProducer::waitFinisher)

    private fun onResultReceived(result: T) = synchronized(this) {
        if (finished) {
            return
        }
        this.result = Box(result)

        listeners.forEach { it.invoke(result) }
        listeners.clear()
    }

    fun await(listener: (T) -> Unit): FinisherDetacher {
        val result = this.result
        if (result != null) {
            listener.invoke(result.value)
            return FakeFinisherDetacher()
        }

        listeners.add(listener)
        return ActiveFinisherDetacher(this, listener)
    }

    fun await(detacher: FinisherDetachers, listener: (T) -> Unit) =
            detacher.addDetacher(await(listener))

    fun weakAwait(listener: (T) -> Unit) = await(
            WeakListener(
                    innerListener = listener,
                    onReferenceEmpty = this::detach
            )
    )

    fun weakAwait(detacher: FinisherDetachers, listener: (T) -> Unit) =
            detacher.addDetacher(weakAwait(listener))

    fun detach(listener: (T) -> Unit) {
        listeners.remove(listener)
    }

    fun awaitSync(): T {
        val existenceResult = result
        if (existenceResult != null) {
            return existenceResult.value
        }

        val latch = CountDownLatch(1)
        await { latch.countDown() }
        latch.await()

        val result = this.result
                ?: throw IllegalStateException("Unexpected null result")

        return result.value
    }

    override fun executeOnCompleted(onCompletedListener: (T) -> Unit) {
        await(onCompletedListener)
    }

    fun fitInTimeOrElse(
            timeOut: TimeValue,
            elseAction: (CancellationException) -> T
    ) = fitInTimeOrElse(
            timeOut = timeOut,
            fitInTimeAction = { it },
            elseAction = elseAction
    )

    fun  fitInTimeOrElse(
            timeOut: TimeValue,
            fitInTimeAction: (T) -> R,
            elseAction: (CancellationException) -> R
    ): Finisher = NewThreadFinisher { onFinished ->
        val countDownLatch = CountDownLatch(1)
        await {
            countDownLatch.countDown()
            val result = fitInTimeAction.invoke(it)
            onFinished.invoke(result)
        }
        countDownLatch.awaitOrElse(timeOut) {
            val ex = CancellationException("Fit in time is over (${timeOut.toMillisecondsString()})")
            val result = elseAction.invoke(ex)
            onFinished.invoke(result)
        }
    }

    @Throws(CancellationException::class)
    fun fitInTimeOrThrow(timeOut: TimeValue) = fitInTimeOrElse(
            timeOut = timeOut,
            elseAction = { throw it }
    )

    override fun toString() =
            "Finisher{${result?.let { "result=$it" }
                    ?: "finished=false, listeners=${listeners.size}"}}"

    /**
     * Additional methods
     */

    companion object {

        inline fun  sync(action: () -> T) =
                forExistenceData(action.invoke())

        fun  forExistenceData(data: T) =
                Finisher { onFinished -> onFinished.invoke(data) }

        fun  auto(): Finisher = AutoFinisher.create()

        fun  fromProducer(
                producer: Producer,
                resultFilter: ((T) -> Boolean)? = null
        ) =
                Finisher { onFinished ->
                    val detachers = ProducerDetachers()
                    producer.attach(detachers) { producedData ->
                        if (resultFilter == null || resultFilter.invoke(producedData)) {
                            detachers.detachAllAndClear()
                            onFinished.invoke(producedData)
                        }
                    }
                }

        fun  fromProducerOrElseIfTimeout(
                producer: Producer,
                timeOut: TimeValue,
                converter: (T) -> R,
                timeoutAction: (CancellationException) -> R,
                resultFilter: ((T) -> Boolean)? = null
        ) =
                NewThreadFinisher { onFinished ->

                    val lock = Any()
                    val detachers = ProducerDetachers()
                    val countDownLatch = CountDownLatch(1)

                    producer.attach(detachers) { producedData ->
                        synchronized(lock) {
                            if (resultFilter == null || resultFilter.invoke(producedData)) {
                                detachers.detachAllAndClear()
                                countDownLatch.countDown()
                                onFinished.invoke(converter.invoke(producedData))
                            }
                        }
                    }

                    countDownLatch.awaitOrElse(timeOut) {
                        synchronized(lock) {
                            detachers.detachAllAndClear()
                            val ex = CancellationException("Fit in time is over (${timeOut.toMillisecondsString()})")
                            onFinished.invoke(timeoutAction.invoke(ex))
                        }
                    }

                }

        fun  fromProducerOrPossibleErrorIfTimeout(
                producer: Producer,
                timeOut: TimeValue,
                resultFilter: ((T) -> Boolean)? = null
        ) = fromProducerOrElseIfTimeout(
                producer = producer,
                converter = { Possible.success(it) },
                timeoutAction = { Possible.error(it) },
                resultFilter = resultFilter,
                timeOut = timeOut
        )

        fun  fromProducerOrThrowIfTimeout(
                producer: Producer,
                timeOut: TimeValue,
                resultFilter: ((T) -> Boolean)? = null
        ) = fromProducerOrElseIfTimeout(
                producer = producer,
                converter = { it },
                timeoutAction = { throw IllegalStateException(it) },
                resultFilter = resultFilter,
                timeOut = timeOut
        )

    }

    fun  mapAsync(additionalAction: (data: T, onFinished: (O) -> Unit) -> Unit): Finisher =
            Finisher { onFinished ->
                await { data ->
                    additionalAction.invoke(data, onFinished)
                }
            }


    fun  map(converter: (T) -> O): Finisher =
            mapAsync { data, onFinished ->
                val result = converter.invoke(data)
                onFinished.invoke(result)
            }

    fun  map(value: O) = map { value }

    fun mapToUnit() = map(Unit)

    fun  attach(other: Finisher): Finisher> =
            Finisher { onFinished ->
                this.await { thisResult ->
                    other.await { otherResult ->
                        onFinished.invoke(thisResult to otherResult)
                    }
                }
            }

    operator fun  plus(other: Finisher) = this.attach(other)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy