@@ -24,12 +24,12 @@ import com.squareup.workflow1.intercept
2424import com.squareup.workflow1.internal.RealRenderContext.RememberStore
2525import com.squareup.workflow1.internal.RealRenderContext.SideEffectRunner
2626import com.squareup.workflow1.trace
27+ import com.squareup.workflow1.workflowSessionToString
2728import kotlinx.coroutines.CancellationException
2829import kotlinx.coroutines.CoroutineName
2930import kotlinx.coroutines.CoroutineScope
3031import kotlinx.coroutines.CoroutineStart.LAZY
3132import kotlinx.coroutines.Job
32- import kotlinx.coroutines.cancel
3333import kotlinx.coroutines.channels.Channel
3434import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
3535import kotlinx.coroutines.launch
@@ -49,38 +49,42 @@ import kotlin.reflect.KType
4949 * structured concurrency).
5050 */
5151@OptIn(WorkflowExperimentalApi ::class , WorkflowExperimentalRuntime ::class )
52- internal class WorkflowNode <PropsT , StateT , OutputT , RenderingT >(
53- val id : WorkflowNodeId ,
52+ internal class StatefulWorkflowNode <PropsT , StateT , OutputT , RenderingT >(
53+ id : WorkflowNodeId ,
5454 workflow : StatefulWorkflow <PropsT , StateT , OutputT , RenderingT >,
5555 initialProps : PropsT ,
5656 snapshot : TreeSnapshot ? ,
5757 baseContext : CoroutineContext ,
5858 // Providing default value so we don't need to specify in test.
5959 override val runtimeConfig : RuntimeConfig = RuntimeConfigOptions .DEFAULT_CONFIG ,
6060 override val workflowTracer : WorkflowTracer ? = null ,
61- private val emitAppliedActionToParent : (ActionApplied <OutputT >) -> ActionProcessingResult =
62- { it },
61+ emitAppliedActionToParent : (ActionApplied <OutputT >) -> ActionProcessingResult = { it },
6362 override val parent : WorkflowSession ? = null ,
64- private val interceptor : WorkflowInterceptor = NoopWorkflowInterceptor ,
63+ interceptor : WorkflowInterceptor = NoopWorkflowInterceptor ,
6564 idCounter : IdCounter ? = null
66- ) : CoroutineScope, SideEffectRunner, RememberStore, WorkflowSession {
67-
68- /* *
69- * Context that has a job that will live as long as this node.
70- * Also adds a debug name to this coroutine based on its ID.
71- */
72- override val coroutineContext = baseContext + Job (baseContext[Job ]) + CoroutineName (id.toString())
73-
74- // WorkflowInstance properties
65+ ) : AbstractWorkflowNode<PropsT, OutputT, RenderingT>(
66+ id = id,
67+ baseContext = baseContext,
68+ interceptor = interceptor,
69+ emitAppliedActionToParent = emitAppliedActionToParent,
70+ ),
71+ SideEffectRunner ,
72+ RememberStore ,
73+ WorkflowSession {
74+
75+ // WorkflowSession properties
7576 override val identifier: WorkflowIdentifier get() = id.identifier
7677 override val renderKey: String get() = id.name
7778 override val sessionId: Long = idCounter.createId()
7879 private var cachedWorkflowInstance: StatefulWorkflow <PropsT , StateT , OutputT , RenderingT >
7980 private var interceptedWorkflowInstance: StatefulWorkflow <PropsT , StateT , OutputT , RenderingT >
8081
82+ override val session: WorkflowSession
83+ get() = this
84+
8185 private val subtreeManager = SubtreeManager (
8286 snapshotCache = snapshot?.childTreeSnapshots,
83- contextForChildren = coroutineContext,
87+ contextForChildren = scope. coroutineContext,
8488 emitActionToParent = ::applyAction,
8589 runtimeConfig = runtimeConfig,
8690 workflowTracer = workflowTracer,
@@ -117,52 +121,54 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
117121 private val context = RenderContext (baseRenderContext, workflow)
118122
119123 init {
120- interceptor.onSessionStarted(this , this )
124+ interceptor.onSessionStarted(workflowScope = scope, session = this )
121125
122126 cachedWorkflowInstance = workflow
123- interceptedWorkflowInstance = interceptor.intercept(cachedWorkflowInstance, this )
124- state = interceptedWorkflowInstance.initialState(initialProps, snapshot?.workflowSnapshot, this )
127+ interceptedWorkflowInstance = interceptor.intercept(
128+ workflow = cachedWorkflowInstance,
129+ workflowSession = this
130+ )
131+ state = interceptedWorkflowInstance.initialState(
132+ props = initialProps,
133+ snapshot = snapshot?.workflowSnapshot,
134+ workflowScope = scope
135+ )
125136 }
126137
127- override fun toString (): String {
128- val parentDescription = parent?.let { " WorkflowInstance(…)" }
129- return " WorkflowInstance(" +
130- " identifier=$identifier , " +
131- " renderKey=$renderKey , " +
132- " instanceId=$sessionId , " +
133- " parent=$parentDescription " +
134- " )"
135- }
138+ override fun toString (): String = workflowSessionToString()
136139
137140 /* *
138141 * Walk the tree of workflows, rendering each one and using
139142 * [RenderContext][com.squareup.workflow1.BaseRenderContext] to give its children a chance to
140143 * render themselves and aggregate those child renderings.
141144 */
142145 @Suppress(" UNCHECKED_CAST" )
143- fun render (
144- workflow : StatefulWorkflow <PropsT , * , OutputT , RenderingT >,
146+ override fun render (
147+ workflow : Workflow <PropsT , OutputT , RenderingT >,
145148 input : PropsT
146- ): RenderingT =
147- renderWithStateType(workflow as StatefulWorkflow <PropsT , StateT , OutputT , RenderingT >, input)
149+ ): RenderingT = renderWithStateType(
150+ workflow = workflow.asStatefulWorkflow() as
151+ StatefulWorkflow <PropsT , StateT , OutputT , RenderingT >,
152+ props = input
153+ )
148154
149155 /* *
150156 * Walk the tree of state machines again, this time gathering snapshots and aggregating them
151157 * automatically.
152158 */
153- fun snapshot (workflow : StatefulWorkflow < * , * , * , * > ): TreeSnapshot {
154- @Suppress( " UNCHECKED_CAST " )
155- val typedWorkflow = workflow as StatefulWorkflow < PropsT , StateT , OutputT , RenderingT >
156- maybeUpdateCachedWorkflowInstance(typedWorkflow )
157- return interceptor.onSnapshotStateWithChildren({
158- val childSnapshots = subtreeManager.createChildSnapshots()
159- val rootSnapshot = interceptedWorkflowInstance.snapshotState(state)
160- TreeSnapshot (
161- workflowSnapshot = rootSnapshot,
162- // Create the snapshots eagerly since subtreeManager is mutable.
163- childTreeSnapshots = { childSnapshots }
164- )
165- }, this )
159+ override fun snapshot (): TreeSnapshot {
160+ return interceptor.onSnapshotStateWithChildren(
161+ proceed = {
162+ val childSnapshots = subtreeManager.createChildSnapshots( )
163+ val rootSnapshot = interceptedWorkflowInstance.snapshotState(state)
164+ TreeSnapshot (
165+ workflowSnapshot = rootSnapshot,
166+ // Create the snapshots eagerly since subtreeManager is mutable.
167+ childTreeSnapshots = { childSnapshots }
168+ )
169+ },
170+ session = this
171+ )
166172 }
167173
168174 override fun runningSideEffect (
@@ -206,20 +212,7 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
206212 return result.lastCalculated as ResultT
207213 }
208214
209- /* *
210- * Register select clauses for the next [result][ActionProcessingResult] from the state machine.
211- *
212- * Walk the tree of state machines, asking each one to wait for its next event. If something happen
213- * that results in an output, that output is returned. Null means something happened that requires
214- * a re-render, e.g. my state changed or a child state changed.
215- *
216- * It is an error to call this method after calling [cancel].
217- *
218- * Contrast this to [applyNextAvailableTreeAction], which is used to check for an action
219- * that is already available without waiting, and then _immediately_ apply it.
220- * The select clauses added here also call [applyAction] when one of them is selected.
221- */
222- fun registerTreeActionSelectors (selector : SelectBuilder <ActionProcessingResult >) {
215+ override fun registerTreeActionSelectors (selector : SelectBuilder <ActionProcessingResult >) {
223216 // Listen for any child workflow updates.
224217 subtreeManager.registerChildActionSelectors(selector)
225218
@@ -231,22 +224,7 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
231224 }
232225 }
233226
234- /* *
235- * Will try to apply any immediately available actions in this action queue or any of our
236- * children's.
237- *
238- * Contrast this to [registerTreeActionSelectors] which will add select clauses that will await
239- * the next action. That will also end up with [applyAction] being called when the clauses is
240- * selected.
241- *
242- * @param skipDirtyNodes Whether or not this should skip over any workflow nodes that are already
243- * 'dirty' - that is, they had their own state changed as the result of a previous action before
244- * the next render pass.
245- *
246- * @return [ActionProcessingResult] of the action processed, or [ActionsExhausted] if there were
247- * none immediately available.
248- */
249- fun applyNextAvailableTreeAction (skipDirtyNodes : Boolean = false): ActionProcessingResult {
227+ override fun applyNextAvailableTreeAction (skipDirtyNodes : Boolean ): ActionProcessingResult {
250228 if (skipDirtyNodes && selfStateDirty) return ActionsExhausted
251229
252230 val result = subtreeManager.applyNextAvailableChildAction(skipDirtyNodes)
@@ -262,11 +240,11 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
262240 /* *
263241 * Cancels this state machine host, and any coroutines started as children of it.
264242 *
265- * This must be called when the caller will no longer call [registerTreeActionSelectors]. It is an error to call [registerTreeActionSelectors]
266- * after calling this method.
243+ * This must be called when the caller will no longer call [registerTreeActionSelectors]. It is an
244+ * error to call [registerTreeActionSelectors] after calling this method.
267245 */
268- fun cancel (cause : CancellationException ? = null ) {
269- coroutineContext .cancel(cause)
246+ override fun cancel (cause : CancellationException ? ) {
247+ super .cancel(cause)
270248 lastRendering = NullableInitBox ()
271249 }
272250
@@ -348,7 +326,6 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
348326 * Applies [action] to this workflow's [state] and then passes the resulting [ActionApplied]
349327 * via [emitAppliedActionToParent] to the parent, with additional information as to whether or
350328 * not this action has changed the current node's state.
351- *
352329 */
353330 private fun applyAction (
354331 action : WorkflowAction <PropsT , StateT , OutputT >,
@@ -389,7 +366,7 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
389366 sideEffect : suspend CoroutineScope .() -> Unit
390367 ): SideEffectNode {
391368 return workflowTracer.trace(" CreateSideEffectNode" ) {
392- val scope = this + CoroutineName (" sideEffect[$key ] for $id " )
369+ val scope = scope + CoroutineName (" sideEffect[$key ] for $id " )
393370 val job = scope.launch(start = LAZY , block = sideEffect)
394371 SideEffectNode (key, job)
395372 }
0 commit comments