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

com.zepben.evolve.services.network.tracing.traversals.BranchRecursiveTraversal.kt Maven / Gradle / Ivy

There is a newer version: 0.24.0rc1
Show newest version
/*
 * Copyright 2020 Zeppelin Bend Pty Ltd
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.zepben.evolve.services.network.tracing.traversals

import java.util.function.Consumer
import java.util.function.Supplier

/**
 * A traversal class that allows you to branch by spawning another traversal instance.
 * I've tried to make this a reusable class, but not sure how useful it is in its current state.
 * It's working for my current use case (set phase tracing) but may need to be rethought to meet requirements of
 * other traversals.
 * Be seriously careful if you ever plan to subclass this class. It would be extremely easy to break it's intended
 * use. Should possibly even be final...
 *
 * @param parent               The traversal that spawned this instance
 * @param queueNext            Function that supplies the next available items to step to from the current item in the traversal.
 * @param processQueueSupplier A supplier of the item queue to be used by the traversal.
 * @param trackerSupplier      A supplier of the item tracker to be used by this traversal.
 * @param branchQueueSupplier  A supplier of the branch queue to be used by this traversal.
 */
@Suppress("MemberVisibilityCanBePrivate")
class BranchRecursiveTraversal(
    private val queueNext: QueueNext,
    private val processQueueSupplier: () -> TraversalQueue,
    private val trackerSupplier: () -> Tracker,
    private val branchQueueSupplier: () -> TraversalQueue>,
    val parent: BranchRecursiveTraversal? = null,
    private val onBranchStart: ((T) -> Unit)? = null
) : Traversal() {

    val queue: TraversalQueue = processQueueSupplier()
    val branchQueue: TraversalQueue> = branchQueueSupplier()
    override val tracker: Tracker = trackerSupplier()

    var branchSupplier: () -> Traversal = defaultBranchSupplier()

    /**
     * Java interop constructor.
     */
    @JvmOverloads
    constructor(
        queueNext: QueueNext,
        processQueueSupplier: Supplier>,
        trackerSupplier: Supplier>,
        branchQueueSupplier: Supplier>>,
        parent: BranchRecursiveTraversal? = null,
        onBranchStart: Consumer? = null
    ) : this(queueNext, processQueueSupplier::get, trackerSupplier::get, branchQueueSupplier::get, parent, { onBranchStart?.accept(it) })

    /**
     * Represents a consumer that takes the current item of the traversal,
     * a function to add items to the traversal queue,
     * a function to add a branch to recurse on,
     * and a supplier that can generate a new Traversal branch.
     *
     * @param T The type of object being traversed.
     */
    fun interface QueueNext {
        fun accept(item: T, traversal: BranchRecursiveTraversal)
    }

    /**
     * @return Whether the item has been visited before.
     */
    fun hasVisited(item: T): Boolean {
        var parent = parent
        while (parent != null) {
            if (parent.tracker.hasVisited(item)) return true
            parent = parent.parent
        }

        return tracker.hasVisited(item)
    }

    /**
     *
     * The default branch supplier to provide the ability to create traversal branches.
     *
     * This supplier creates a new [BranchRecursiveTraversal] using the suppliers provided to this instance when
     * it was created and setting this as the parent.
     *
     * It then copies all step actions and stop conditions into the new instance.
     * where its creator has already been.
     *
     * @return The default branch supplier used by this class.
     */
    fun defaultBranchSupplier(): () -> Traversal = {
        BranchRecursiveTraversal(
            queueNext,
            processQueueSupplier,
            trackerSupplier,
            branchQueueSupplier,
            this,
            onBranchStart
        )
            .copyStepActions(this)
            .copyStopConditions(this)
    }

    override fun reset(): BranchRecursiveTraversal {
        resetRunFlag()

        queue.clear()
        branchQueue.clear()
        tracker.clear()

        return this
    }

    /**
     * Runs the traversal. All items in the queue will be processed before moving onto processing the branch
     * queue. The results from each of the branch traversals are added to the result of this traversal to give all
     * items that were traversed through all branches.
     */
    override fun doRun(canStopOnStartItem: Boolean) {
        var canStop = true

        startItem?.let {
            queue.add(it)
            canStop = canStopOnStartItem
        }

        while (queue.hasNext()) {
            queue.next()?.let {
                if (visit(it)) {
                    val isStopping = canStop && matchesAnyStopCondition(it)

                    applyStepActions(it, isStopping)

                    if (!isStopping)
                        queueNext.accept(it, this)

                    canStop = true
                }
            }
        }

        traverseBranches()
    }

    private fun visit(item: T): Boolean {
        var parent = parent
        while (parent != null) {
            if (parent.tracker.hasVisited(item))
                return false
            parent = parent.parent
        }

        return tracker.visit(item)
    }

    private fun traverseBranches() {
        while (branchQueue.hasNext()) {
            branchQueue.next()?.let { traversal ->
                traversal.startItem?.let {
                    onBranchStart?.invoke(it)
                    traversal.run()
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy