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

com.zepben.evolve.services.network.tracing.traversals.Traversal.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

/**
 *
 * Base class that provides some common functionality for traversals. This includes things like registering callbacks
 * to be called at every step in the traversal as well as registering stop conditions that traversals can check for when
 * to stop following a path.
 *
 * This base class does not actually provide any way to traverse the items. It needs to be implemented in
 * subclasses. See [BasicTraversal] for an example.
 *
 * Note this class is not thread safe!
 *
 * @param T Object type to be traversed.
 */
abstract class Traversal {

    /**
     * The item the traversal will start at, or `null` if it has not been set.
     */
    var startItem: T? = null

    abstract val tracker: Tracker

    @Volatile
    private var running = false

    @Volatile
    private var hasRun = false
    private val stopConditions = mutableListOf<(T) -> Boolean>()
    private val stepActions = mutableListOf>()

    /**
     *
     * Add a callback to check whether the current item in the traversal is a stop point.
     *
     * If any of the registered stop conditions return true, the traversal will not call the callback to queue more items.
     * Note that a match on a stop condition doesn't necessarily stop the traversal, it just stops
     * traversal of the current branch.
     *
     * @param condition A predicate that if returns true will cause the traversal to stop traversing the branch.
     * @return this traversal instance.
     */
    fun addStopCondition(condition: (T) -> Boolean): Traversal {
        stopConditions.add(condition)
        return this
    }

    /**
     * Clears all the stop conditions registered on this traversal.
     */
    fun clearStopConditions(): Traversal {
        stopConditions.clear()
        return this
    }

    /**
     * Copies all the stop conditions from another traversal to this traversal
     *
     * @param other The other traversal object to copy from.
     * @return this traversal instance.
     */
    fun copyStopConditions(other: Traversal): Traversal {
        stopConditions.addAll(other.stopConditions)
        return this
    }

    /**
     * Checks all the stop conditions for the passed in item and returns true if any match.
     * This calls all registered stop conditions even if one has already returned true to make sure everything is
     * notified about this item.
     *
     * @param item The item to pass to the stop conditions.
     * @return true if any of the stop conditions return true.
     */
    fun matchesAnyStopCondition(item: T): Boolean = stopConditions.fold(false) { stop, condition -> stop or condition(item) }

    /**
     * Add a callback which is called for every item in the traversal (including the starting item).
     *
     * @param action Action to be called on each item in the traversal, passing if the trace will stop on this step.
     * @return this traversal instance.
     */
    fun addStepAction(action: StepAction): Traversal {
        stepActions.add(action)
        return this
    }

    /**
     * Add a callback which is called for every item in the traversal (including the starting item).
     *
     * @param action Action to be called on each item in the traversal, without passing if the trace will stop on this step.
     * @return this traversal instance.
     */
    fun addStepAction(action: Consumer): Traversal {
        stepActions.add { it, _ -> action.accept(it) }
        return this
    }

    /**
     * Add a callback which is called for every item in the traversal (including the starting item).
     *
     * @param action Action to be called on each item in the traversal, without passing if the trace will stop on this step.
     * @return this traversal instance.
     */
    fun addStepAction(action: (item: T) -> Unit): Traversal {
        stepActions.add { it, _ -> action(it) }
        return this
    }

    /**
     * Add a callback which is called for every item in the traversal that does not match a stop condition (including the starting item).
     *
     * @param action Action to be called on each item in the traversal that is not being stopped on.
     * @return this traversal instance.
     */
    fun ifNotStopping(action: (item: T) -> Unit): Traversal {
        stepActions.add { it, isStopping -> if (!isStopping) action(it) }
        return this
    }

    /**
     * Add a callback which is called for every item in the traversal that matches a stop condition (including the starting item).
     *
     * @param action Action to be called on each item in the traversal that is being stopped on.
     * @return this traversal instance.
     */
    fun ifStopping(action: (item: T) -> Unit): Traversal {
        stepActions.add { it, isStopping -> if (isStopping) action(it) }
        return this
    }

    /**
     * Clears all step actions registered on this traversal.
     */
    fun clearStepActions(): Traversal {
        stepActions.clear()
        return this
    }

    /**
     * Copies all the step actions from the passed in traversal to this traversal.
     *
     * @param other The other traversal object to copy from.
     * @return this traversal instance.
     */
    fun copyStepActions(other: Traversal): Traversal {
        stepActions.addAll(other.stepActions)
        return this
    }

    /**
     * Calls all the step actions with the passed in item.
     *
     * @param item       The item to pass to the step actions.
     * @param isStopping Indicates if the trace will stop on this step.
     */
    fun applyStepActions(item: T, isStopping: Boolean): Traversal {
        stepActions.forEach { it.apply(item, isStopping) }
        return this
    }

    /**
     * Sets the item the traversal will start at.
     *
     * @param item The item to start at.
     * @return this traversal instance.
     */
    fun setStart(item: T): Traversal {
        startItem = item
        return this
    }

    /**
     * Starts the traversal calling [setStart] on the [start] parameter before running.
     *
     * @param start              The item to start at.
     * @param canStopOnStartItem Indicates if the traversal will check the start item for stop conditions.
     */
    @JvmOverloads
    fun run(start: T, canStopOnStartItem: Boolean = true) {
        setStart(start)
        run(canStopOnStartItem)
    }

    /**
     * Starts the traversal. [setStart] should have been called to set the starting item or use the
     * overloaded run method that takes an item to start at.
     *
     * @param canStopOnStartItem Indicates if the traversal will check the start item for stop conditions.
     */
    @JvmOverloads
    fun run(canStopOnStartItem: Boolean = true) {
        check(!running) { "Traversal is already running." }
        check(!hasRun) { "Traversal must be reset before reuse." }

        running = true
        hasRun = true

        doRun(canStopOnStartItem)

        running = false
    }

    protected fun resetRunFlag() {
        check(!running) { "Traversal is currently running." }
        hasRun = false
    }

    abstract fun reset(): Traversal
    protected abstract fun doRun(canStopOnStartItem: Boolean)

    fun interface StepAction {

        fun apply(item: T, isStopping: Boolean)

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy