77namespace SQuan . Helpers . Maui ;
88
99/// <summary>
10- /// Manages a graph of <see cref="ExpressionNode{T} "/> instances and evaluates expressions
10+ /// Manages a graph of <see cref="ExpressionNode"/> instances and evaluates expressions
1111/// asynchronously using a background calculation loop.
1212/// The manager maintains dependency relationships between nodes, propagates value changes,
1313/// and ensures calculations are performed in the correct order.
@@ -45,9 +45,9 @@ public static ILogger? Logger
4545 /// </summary>
4646 public readonly ExpressionParserPlugin ParserPlugin = new ( ) ;
4747
48- readonly ConcurrentDictionary < string , IExpressionNode > expressions = new ( ) ;
49- readonly BlockingCollection < IExpressionWorkItem > queuedWork = new ( ) ;
50- readonly ConcurrentDictionary < IExpressionNode , byte > queuedNodes = new ( ) ;
48+ readonly ConcurrentDictionary < string , ExpressionNode > expressions = new ( ) ;
49+ readonly BlockingCollection < ExpressionWorkItem > queuedWork = new ( ) ;
50+ readonly ConcurrentDictionary < ExpressionNode , byte > queuedNodes = new ( ) ;
5151 readonly WeakEventManager propertyChangedEventManager = new ( ) ;
5252
5353 volatile bool isRunning = false ;
@@ -115,24 +115,17 @@ public object? this[string nodeRef]
115115 {
116116 get
117117 {
118- if ( expressions . TryGetValue ( nodeRef , out var node ) && node is IExpressionNode _node )
118+ if ( expressions . TryGetValue ( nodeRef , out var node ) && node is ExpressionNode _node )
119119 {
120120 return _node . GetInternalValue ( ) ;
121121 }
122122 return null ;
123123 }
124124 set
125125 {
126- if ( expressions . TryGetValue ( nodeRef , out var node ) && node is IExpressionNode _node )
126+ if ( expressions . TryGetValue ( nodeRef , out var node ) && node is ExpressionNode _node )
127127 {
128- IExpressionNode ? _ = _node switch
129- {
130- ExpressionNode < int > nodeI => SetValue ( nodeI , value , ExpressionValueKind . UserInput ) ,
131- ExpressionNode < long > nodeL => SetValue ( nodeL , value , ExpressionValueKind . UserInput ) ,
132- ExpressionNode < double > nodeD => SetValue ( nodeD , value , ExpressionValueKind . UserInput ) ,
133- ExpressionNode < string > nodeS => SetValue ( nodeS , value , ExpressionValueKind . UserInput ) ,
134- _ => null
135- } ;
128+ SetValue ( _node , value , ExpressionValueKind . UserInput ) ;
136129 }
137130 }
138131 }
@@ -155,26 +148,26 @@ public object? this[string nodeRef]
155148 /// <returns>The current value, or null if the node does not exist.</returns>
156149 public object ? GetValue ( string nodeRef )
157150 {
158- if ( expressions . TryGetValue ( nodeRef , out var node ) && node is IExpressionNode _node )
151+ if ( expressions . TryGetValue ( nodeRef , out var node ) && node is ExpressionNode _node )
159152 {
160153 return _node . GetInternalValue ( ) ;
161154 }
162155 return null ;
163156 }
164157
165158 /// <summary>
166- /// Creates a new instance of the <see cref="ExpressionNode{T} "/> class associated with the specified node reference.
159+ /// Creates a new instance of the <see cref="ExpressionNode"/> class associated with the specified node reference.
167160 /// </summary>
168161 /// <param name="nodeRef">The unique identifier for the node to create. Cannot be null.</param>
169- /// <returns>A new <see cref="ExpressionNode{T} "/> instance associated with the specified node reference.</returns>
170- public ExpressionNode < T > CreateNode < T > ( string nodeRef )
162+ /// <returns>A new <see cref="ExpressionNode"/> instance associated with the specified node reference.</returns>
163+ public ExpressionNode CreateNode < T > ( string nodeRef )
171164 {
172- if ( expressions . TryGetValue ( nodeRef , out var _node ) && _node is ExpressionNode < T > __node )
165+ if ( expressions . TryGetValue ( nodeRef , out var _node ) && _node is ExpressionNode __node )
173166 {
174167 __node . Clear ( ) ;
175168 }
176169
177- var node = new ExpressionNode < T >
170+ var node = new ExpressionNode
178171 {
179172 Owner = this ,
180173 NodeRef = nodeRef ,
@@ -184,9 +177,9 @@ public ExpressionNode<T> CreateNode<T>(string nodeRef)
184177 return node ;
185178 }
186179
187- ExpressionNode < T > GetOrCreateNode < T > ( string nodeRef )
180+ ExpressionNode GetOrCreateNode < T > ( string nodeRef )
188181 {
189- if ( expressions . TryGetValue ( nodeRef , out var _node ) && _node is ExpressionNode < T > __node )
182+ if ( expressions . TryGetValue ( nodeRef , out var _node ) && _node is ExpressionNode __node )
190183 {
191184 return __node ;
192185 }
@@ -202,18 +195,17 @@ ExpressionNode<T> GetOrCreateNode<T>(string nodeRef)
202195 /// <param name="value">The value to assign.</param>
203196 /// <param name="kind">The reason for the value change.</param>
204197 /// <returns>The affected node.</returns>
205- public ExpressionNode < T > SetValue < T > ( string nodeRef , object ? value , ExpressionValueKind kind = ExpressionValueKind . Default )
206- => SetValue < T > ( GetOrCreateNode < T > ( nodeRef ) , value , kind ) ;
198+ public ExpressionNode SetValue < T > ( string nodeRef , object ? value , ExpressionValueKind kind = ExpressionValueKind . Default )
199+ => SetValue ( GetOrCreateNode < T > ( nodeRef ) , value , kind ) ;
207200
208201 /// <summary>
209202 /// Sets the value of a node with an explicit value type.
210203 /// </summary>
211- /// <typeparam name="T">The expected value type.</typeparam>
212204 /// <param name="node">The node to update.</param>
213205 /// <param name="value">The value to assign.</param>
214206 /// <param name="kind">The reason for the value change.</param>
215207 /// <returns>The updated node.</returns>
216- public ExpressionNode < T > SetValue < T > ( ExpressionNode < T > node , object ? value , ExpressionValueKind kind = ExpressionValueKind . Default )
208+ public ExpressionNode SetValue ( ExpressionNode node , object ? value , ExpressionValueKind kind = ExpressionValueKind . Default )
217209 {
218210 if ( node . ValueKind == ExpressionValueKind . PendingParsing )
219211 {
@@ -232,7 +224,7 @@ public ExpressionNode<T> SetValue<T>(ExpressionNode<T> node, object? value, Expr
232224
233225 foreach ( var outputKey in node . OutputNodeRefs . Keys )
234226 {
235- if ( expressions . TryGetValue ( outputKey , out var outputNode ) && outputNode is IExpressionNode _outputNode )
227+ if ( expressions . TryGetValue ( outputKey , out var outputNode ) && outputNode is ExpressionNode _outputNode )
236228 {
237229 //Logger?.LogTrace("Notifying output node {OutputNodeRef} of change in {NodeRef}", outputKey, node.NodeRef);
238230 Recalculate ( _outputNode ) ;
@@ -249,17 +241,17 @@ public ExpressionNode<T> SetValue<T>(ExpressionNode<T> node, object? value, Expr
249241 /// <param name="nodeRef">The node reference.</param>
250242 /// <param name="expression">The expression text.</param>
251243 /// <returns>The affected node.</returns>
252- public ExpressionNode < T > SetExpression < T > ( string nodeRef , string expression )
244+ public ExpressionNode SetExpression < T > ( string nodeRef , string expression )
253245 => SetExpression < T > ( GetOrCreateNode < T > ( nodeRef ) , expression ) ;
254246
255- ExpressionNode < T > SetExpression < T > ( ExpressionNode < T > node , string expression )
247+ ExpressionNode SetExpression < T > ( ExpressionNode node , string expression )
256248 {
257249 Logger ? . LogTrace ( "SetExpression {NodeRef} to {Expression} (type={ValueType})" , node . NodeRef , expression , typeof ( T ) ) ;
258250 node . Expression = expression ;
259251 node . ValueKind = ExpressionValueKind . PendingParsing ;
260252 if ( queuedNodes . TryAdd ( node , 0 ) )
261253 {
262- queuedWork . Add ( new ExpressionWorkItemInitialize { Node = node } ) ;
254+ queuedWork . Add ( new ExpressionWorkItem { Node = node } ) ;
263255 }
264256 return node ;
265257 }
@@ -271,17 +263,17 @@ ExpressionNode<T> SetExpression<T>(ExpressionNode<T> node, string expression)
271263 /// <param name="nodeRef">The node reference.</param>
272264 /// <param name="expression">The default expression text.</param>
273265 /// <returns>The affected node.</returns>
274- public ExpressionNode < T > SetDefault < T > ( string nodeRef , string expression )
266+ public ExpressionNode SetDefault < T > ( string nodeRef , string expression )
275267 => SetDefault < T > ( GetOrCreateNode < T > ( nodeRef ) , expression ) ;
276268
277- ExpressionNode < T > SetDefault < T > ( ExpressionNode < T > node , string expression )
269+ ExpressionNode SetDefault < T > ( ExpressionNode node , string expression )
278270 {
279271 Logger ? . LogTrace ( "SetDefault {NodeRef} to {Expression} (type={ValueType})" , node . NodeRef , expression , typeof ( T ) ) ;
280272 node . DefaultExpression = expression ;
281273 node . ValueKind = ExpressionValueKind . PendingParsing ;
282274 if ( queuedNodes . TryAdd ( node , 0 ) )
283275 {
284- queuedWork . Add ( new ExpressionWorkItemInitialize { Node = node } ) ;
276+ queuedWork . Add ( new ExpressionWorkItem { Node = node } ) ;
285277 }
286278 return node ;
287279 }
@@ -292,7 +284,7 @@ ExpressionNode<T> SetDefault<T>(ExpressionNode<T> node, string expression)
292284 /// <param name="nodeRef">The node reference.</param>
293285 public void Recalculate ( string nodeRef )
294286 {
295- if ( expressions . TryGetValue ( nodeRef , out var node ) && node is IExpressionNode _node )
287+ if ( expressions . TryGetValue ( nodeRef , out var node ) && node is ExpressionNode _node )
296288 {
297289 Recalculate ( _node ) ;
298290 }
@@ -302,7 +294,7 @@ public void Recalculate(string nodeRef)
302294 /// Enqueues a specific node for recalculation.
303295 /// </summary>
304296 /// <param name="node">The node to recalculate.</param>
305- public void Recalculate ( IExpressionNode node )
297+ public void Recalculate ( ExpressionNode node )
306298 {
307299 switch ( node . ValueKind )
308300 {
@@ -327,7 +319,7 @@ public void Recalculate(IExpressionNode node)
327319
328320 if ( queuedNodes . TryAdd ( node , 0 ) )
329321 {
330- queuedWork . Add ( new ExpressionWorkItemCalculate { Node = node } ) ;
322+ queuedWork . Add ( new ExpressionWorkItem { Node = node } ) ;
331323 }
332324 }
333325
@@ -337,7 +329,7 @@ public void Recalculate(IExpressionNode node)
337329 /// <param name="nodeRef">The expression node to reset. This node must not be in an error state to be reset successfully.</param>
338330 public void ResetToDefault ( string nodeRef )
339331 {
340- if ( expressions . TryGetValue ( nodeRef , out var node ) && node is IExpressionNode _node )
332+ if ( expressions . TryGetValue ( nodeRef , out var node ) && node is ExpressionNode _node )
341333 {
342334 ResetToDefault ( _node ) ;
343335 }
@@ -347,7 +339,7 @@ public void ResetToDefault(string nodeRef)
347339 /// Resets the specified expression node to a pending reset state if it is in a valid state.
348340 /// </summary>
349341 /// <param name="node">The expression node to reset. This node must not be in an error state to be reset successfully.</param>
350- public void ResetToDefault ( IExpressionNode node )
342+ public void ResetToDefault ( ExpressionNode node )
351343 {
352344 switch ( node . ValueKind )
353345 {
@@ -371,7 +363,7 @@ public void ResetToDefault(IExpressionNode node)
371363
372364 if ( queuedNodes . TryAdd ( node , 0 ) )
373365 {
374- queuedWork . Add ( new ExpressionWorkItemReset { Node = node } ) ;
366+ queuedWork . Add ( new ExpressionWorkItem { Node = node } ) ;
375367 }
376368 }
377369
@@ -399,28 +391,8 @@ public void StartWorkLoop(CancellationToken ct)
399391 try
400392 {
401393 var workItem = queuedWork . Take ( ct ) ;
402- if ( workItem is ExpressionWorkItemNode workItemNode )
403- {
404- queuedNodes . TryRemove ( workItemNode . Node , out _ ) ;
405- }
406-
407- if ( workItem is ExpressionWorkItemInitialize init )
408- {
409- _ = InitializeNow ( init . Node , ct ) ;
410- continue ;
411- }
412-
413- if ( workItem is ExpressionWorkItemCalculate calculate )
414- {
415- _ = CalculateNow ( calculate . Node , ct ) ;
416- continue ;
417- }
418-
419- if ( workItem is ExpressionWorkItemReset reset )
420- {
421- _ = ResetNow ( reset . Node , ct ) ;
422- continue ;
423- }
394+ ExpressionNode node = workItem . Node ;
395+ queuedNodes . TryRemove ( node , out _ ) ;
424396
425397 if ( workItem is ExpressionWorkItemQuit quit )
426398 {
@@ -436,6 +408,36 @@ public void StartWorkLoop(CancellationToken ct)
436408 isRunning = false ;
437409 break ;
438410 }
411+
412+ switch ( node . ValueKind )
413+ {
414+ case ExpressionValueKind . Uninitialized :
415+ case ExpressionValueKind . PendingParsing :
416+ if ( ! InitializeNow ( node , ct ) )
417+ {
418+ Logger ? . LogError ( "Failed to initialize {NodeRef}'s expression {Expression}" , node . NodeRef , node . Expression ) ;
419+ continue ;
420+ }
421+ CalculateNow ( node , ct ) ;
422+ continue ;
423+ case ExpressionValueKind . Folder :
424+ case ExpressionValueKind . UserInput :
425+ case ExpressionValueKind . Calculated :
426+ case ExpressionValueKind . PendingCalculation :
427+ CalculateNow ( node , ct ) ;
428+ continue ;
429+ case ExpressionValueKind . PendingReset :
430+ case ExpressionValueKind . Default :
431+ ResetNow ( node , ct ) ;
432+ break ;
433+ case ExpressionValueKind . ResetError :
434+ case ExpressionValueKind . ParseError :
435+ case ExpressionValueKind . CalculateError :
436+ default :
437+ // Do nothing, won't be re-enqueued
438+ Logger ? . LogWarning ( "Node {NodeRef} is in error state ({ValueKind}), skipping calculation" , node . NodeRef , node . ValueKind ) ;
439+ break ;
440+ }
439441 }
440442 catch ( OperationCanceledException )
441443 {
@@ -452,7 +454,7 @@ public void StartWorkLoop(CancellationToken ct)
452454 } , ct ) ;
453455 }
454456
455- bool InitializeNow ( IExpressionNode node , CancellationToken ct )
457+ bool InitializeNow ( ExpressionNode node , CancellationToken ct )
456458 {
457459 var parser = new ExpressionParser ( ParserPlugin ) ;
458460 if ( ! string . IsNullOrEmpty ( node . DefaultExpression ) )
@@ -476,11 +478,10 @@ bool InitializeNow(IExpressionNode node, CancellationToken ct)
476478 node . SetTokens ( parser . Tokens ) ;
477479 UpdateDependencyGraph ( node ) ;
478480 }
479-
480- return CalculateNow ( node , ct ) ;
481+ return true ;
481482 }
482483
483- bool CalculateNow ( IExpressionNode node , CancellationToken ct )
484+ bool CalculateNow ( ExpressionNode node , CancellationToken ct )
484485 {
485486 switch ( node . ValueKind )
486487 {
@@ -518,7 +519,7 @@ bool CalculateNow(IExpressionNode node, CancellationToken ct)
518519 return true ;
519520 }
520521
521- bool ResetNow ( IExpressionNode node , CancellationToken ct )
522+ bool ResetNow ( ExpressionNode node , CancellationToken ct )
522523 {
523524 switch ( node . ValueKind )
524525 {
@@ -569,12 +570,12 @@ public async Task StopCalculationLoopAsync()
569570 }
570571
571572 isRunning = false ;
572- queuedWork . Add ( new ExpressionWorkItemQuit { RunId = currentRunId } ) ;
573+ queuedWork . Add ( new ExpressionWorkItemQuit { RunId = currentRunId , Node = ExpressionNode . Empty } ) ;
573574 await task ;
574575 runningTask = null ;
575576 }
576577
577- void UpdateDependencyGraph ( IExpressionNode node )
578+ void UpdateDependencyGraph ( ExpressionNode node )
578579 {
579580 foreach ( var inputNodeRef in node . GetInputNodeRefs ( ) )
580581 {
0 commit comments