Major changes in the flow of code, implemented modularization of the code for maintainability
Some checks failed
Build / build (push) Has been cancelled
Some checks failed
Build / build (push) Has been cancelled
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using Nodify.Calculator.NodeHandlers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@@ -34,54 +35,9 @@ namespace Nodify.Calculator
|
||||
LocationY = op.Location.Y
|
||||
};
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case TakeOperationViewModel take:
|
||||
nodeData.NodeType = "System";
|
||||
nodeData.SystemOp = take.SystemOperationType.ToString();
|
||||
nodeData.NthIndex = take.NthIndex;
|
||||
nodeData.IsRandom = take.IsRandom;
|
||||
break;
|
||||
case AuthOperationViewModel auth:
|
||||
nodeData.NodeType = "System";
|
||||
nodeData.SystemOp = auth.SystemOperationType.ToString();
|
||||
nodeData.AuthBaseUrl = auth.BaseUrl ?? string.Empty;
|
||||
nodeData.AuthType = auth.AuthType ?? string.Empty;
|
||||
break;
|
||||
case FunctionOperationViewModel func:
|
||||
nodeData.NodeType = "Function";
|
||||
nodeData.FunctionName = func.FunctionName;
|
||||
foreach (var p in func.InputParameters)
|
||||
nodeData.FunctionInputs.Add(new FunctionParamData { Name = p.Name, Type = p.Type });
|
||||
foreach (var p in func.OutputParameters)
|
||||
nodeData.FunctionOutputs.Add(new FunctionParamData { Name = p.Name, Type = p.Type });
|
||||
break;
|
||||
case SystemOperationViewModel sys:
|
||||
nodeData.NodeType = "System";
|
||||
nodeData.SystemOp = sys.SystemOperationType.ToString();
|
||||
// For GET_SET nodes, save ClassName and variable metadata
|
||||
if (sys.SystemOperationType == SystemOperations.GET_SET)
|
||||
{
|
||||
nodeData.ClassName = sys.ClassName ?? string.Empty;
|
||||
nodeData.IsSimpleVariable = sys.IsSimpleVariable;
|
||||
nodeData.VariableType = sys.VariableType ?? string.Empty;
|
||||
}
|
||||
break;
|
||||
case APIOperationViewModel api:
|
||||
nodeData.NodeType = "API";
|
||||
nodeData.OPType = api.OperationType;
|
||||
nodeData.ResponseModelClassName = api.ResponseModelClassName;
|
||||
break;
|
||||
case ExpandoOperationViewModel:
|
||||
nodeData.NodeType = "Expando";
|
||||
break;
|
||||
case CalculatorOperationViewModel:
|
||||
nodeData.NodeType = "Calculator";
|
||||
break;
|
||||
default:
|
||||
nodeData.NodeType = "Normal";
|
||||
break;
|
||||
}
|
||||
// Delegate to the handler for VM-specific properties
|
||||
var handler = NodeHandlerRegistry.FindForSave(op);
|
||||
handler?.Save(op, nodeData);
|
||||
|
||||
// Save connector metadata for all nodes
|
||||
foreach (var c in op.Input)
|
||||
@@ -179,16 +135,17 @@ namespace Nodify.Calculator
|
||||
calculator.IsLoading = true;
|
||||
try
|
||||
{
|
||||
|
||||
var nodeMap = new Dictionary<string, OperationViewModel>();
|
||||
|
||||
// Recreate nodes
|
||||
// Recreate nodes — each handler knows how to restore its own type
|
||||
foreach (var nd in graph.Nodes)
|
||||
{
|
||||
OperationViewModel op = null;
|
||||
try
|
||||
{
|
||||
op = RecreateNode(nd, opsMenu);
|
||||
var handler = NodeHandlerRegistry.FindForRestore(nd);
|
||||
if (handler != null)
|
||||
op = handler.Restore(nd);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -227,292 +184,6 @@ namespace Nodify.Calculator
|
||||
}
|
||||
}
|
||||
|
||||
private static OperationViewModel RecreateNode(NodeData nd, OperationsMenuViewModel opsMenu)
|
||||
{
|
||||
switch (nd.NodeType)
|
||||
{
|
||||
case "API":
|
||||
{
|
||||
// Find matching swagger operation or create from saved data
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = nd.Title,
|
||||
OPType = nd.OPType?.ToLower() ?? "get",
|
||||
Type = OperationType.API,
|
||||
ResponseModelClassName = nd.ResponseModelClassName ?? string.Empty
|
||||
};
|
||||
// Restore inputs from saved connector data (skip flow triangle at index 0)
|
||||
foreach (var ic in nd.InputConnectors)
|
||||
{
|
||||
if (ic.Shape != "Triangle")
|
||||
info.Input.Add(ic.Title);
|
||||
}
|
||||
info.Output.Add("");
|
||||
return OperationFactory.GetOperation(info);
|
||||
}
|
||||
|
||||
case "System":
|
||||
{
|
||||
if (Enum.TryParse<SystemOperations>(nd.SystemOp, out var sysOp))
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = nd.Title,
|
||||
Type = OperationType.System,
|
||||
sysOp = sysOp
|
||||
};
|
||||
|
||||
if (sysOp == SystemOperations.GET_SET && !string.IsNullOrEmpty(nd.ClassName))
|
||||
{
|
||||
info.ClassName = nd.ClassName;
|
||||
info.IsSimpleVariable = nd.IsSimpleVariable;
|
||||
info.VariableType = nd.VariableType ?? string.Empty;
|
||||
info.IsModelNode = true;
|
||||
// Extract just "GET" or "SET" prefix so the factory doesn't re-append ClassName
|
||||
var savedTitle = nd.Title ?? "";
|
||||
if (savedTitle.StartsWith("GET ")) info.Title = "GET";
|
||||
else if (savedTitle.StartsWith("SET ")) info.Title = "SET";
|
||||
}
|
||||
|
||||
// Set up inputs/outputs based on system operation type
|
||||
switch (sysOp)
|
||||
{
|
||||
case SystemOperations.BEGIN:
|
||||
info.Output.Add(""); // flow output only
|
||||
break;
|
||||
case SystemOperations.END:
|
||||
info.Input.Add(""); // flow input only
|
||||
break;
|
||||
case SystemOperations.DEBUG:
|
||||
info.Input.Add("Value");
|
||||
info.IsFlowNode = true;
|
||||
break;
|
||||
case SystemOperations.TAKE:
|
||||
info.Input.Add("List");
|
||||
break;
|
||||
case SystemOperations.COPY:
|
||||
info.Input.Add("");
|
||||
break;
|
||||
case SystemOperations.SPLIT:
|
||||
info.Input.Add("");
|
||||
break;
|
||||
case SystemOperations.NEW_OBJECT:
|
||||
info.Input.Add("");
|
||||
info.IsFlowNode = true;
|
||||
break;
|
||||
case SystemOperations.IF:
|
||||
info.Output.Add("");
|
||||
info.IsFlowNode = true;
|
||||
break;
|
||||
case SystemOperations.KNOT:
|
||||
// Knot nodes manage their own connectors; don't add any here
|
||||
break;
|
||||
case SystemOperations.GET_SET:
|
||||
// GET_SET nodes build their own connectors in OperationFactory
|
||||
break;
|
||||
default:
|
||||
info.Output.Add("");
|
||||
info.IsFlowNode = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var op = OperationFactory.GetOperation(info);
|
||||
|
||||
// Restore Take properties
|
||||
if (op is TakeOperationViewModel takeOp)
|
||||
{
|
||||
takeOp.NthIndex = nd.NthIndex;
|
||||
takeOp.IsRandom = nd.IsRandom;
|
||||
}
|
||||
|
||||
// Restore dynamic connectors for SPLIT/COPY/TAKE from saved data
|
||||
RestoreDynamicConnectors(op, nd);
|
||||
|
||||
return op;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
case "Function":
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = nd.FunctionName,
|
||||
Type = OperationType.System,
|
||||
sysOp = SystemOperations.FUNCTION,
|
||||
IsFunction = true
|
||||
};
|
||||
foreach (var inp in nd.FunctionInputs)
|
||||
info.FunctionInputs.Add(new FunctionParameterInfo { Name = inp.Name, Type = inp.Type });
|
||||
foreach (var outp in nd.FunctionOutputs)
|
||||
info.FunctionOutputs.Add(new FunctionParameterInfo { Name = outp.Name, Type = outp.Type });
|
||||
info.Output.Add("");
|
||||
return OperationFactory.GetOperation(info);
|
||||
}
|
||||
|
||||
case "Expando":
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = nd.Title,
|
||||
Type = OperationType.Expando
|
||||
};
|
||||
// Restore input count from saved connectors
|
||||
foreach (var ic in nd.InputConnectors)
|
||||
info.Input.Add(ic.Title);
|
||||
info.Output.Add("");
|
||||
return OperationFactory.GetOperation(info);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = nd.Title,
|
||||
Type = OperationType.Normal
|
||||
};
|
||||
foreach (var ic in nd.InputConnectors)
|
||||
info.Input.Add(ic.Title);
|
||||
info.Output.Add("");
|
||||
return OperationFactory.GetOperation(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RestoreDynamicConnectors(OperationViewModel op, NodeData nd)
|
||||
{
|
||||
// For SPLIT nodes: if saved outputs have more than just the flow triangle,
|
||||
// restore the dynamic property outputs
|
||||
if (op is SystemOperationViewModel sysVm)
|
||||
{
|
||||
if (sysVm.SystemOperationType == SystemOperations.SPLIT)
|
||||
{
|
||||
// Remove default non-triangle outputs, then add saved ones
|
||||
var defaultNonTriangle = op.Output.Where(o => o.Shape != ConnectorShape.Triangle).ToList();
|
||||
foreach (var d in defaultNonTriangle) op.Output.Remove(d);
|
||||
|
||||
foreach (var sc in nd.OutputConnectors)
|
||||
{
|
||||
if (sc.Shape == "Triangle") continue;
|
||||
op.Output.Add(DeserializeConnector(sc, false));
|
||||
}
|
||||
// Update title from saved
|
||||
sysVm.Title = nd.Title;
|
||||
}
|
||||
else if (sysVm.SystemOperationType == SystemOperations.NEW_OBJECT)
|
||||
{
|
||||
// Restore dynamic property inputs (keep triangle + square, replace the rest)
|
||||
var dynamicInputs = op.Input.Where(i => i.Shape != ConnectorShape.Triangle && i.Shape != ConnectorShape.Square).ToList();
|
||||
foreach (var d in dynamicInputs) op.Input.Remove(d);
|
||||
|
||||
foreach (var ic in nd.InputConnectors)
|
||||
{
|
||||
if (ic.Shape == "Triangle" || ic.Shape == "Square") continue;
|
||||
op.Input.Add(DeserializeConnector(ic, true));
|
||||
}
|
||||
|
||||
// Restore output connector metadata (Square model output)
|
||||
var savedModelOutput = nd.OutputConnectors.FirstOrDefault(o => o.Shape == "Square");
|
||||
var modelOutput = op.Output.FirstOrDefault(o => o.Shape == ConnectorShape.Square);
|
||||
if (savedModelOutput != null && modelOutput != null)
|
||||
{
|
||||
modelOutput.Title = savedModelOutput.Title;
|
||||
modelOutput.DataType = savedModelOutput.DataType;
|
||||
modelOutput.ConnectorColor = Color.FromArgb(savedModelOutput.ColorArgb);
|
||||
}
|
||||
|
||||
sysVm.Title = nd.Title;
|
||||
}
|
||||
else if (sysVm.SystemOperationType == SystemOperations.COPY)
|
||||
{
|
||||
// Restore adapted connectors
|
||||
if (nd.InputConnectors.Count > 0 && nd.InputConnectors[0].Shape != "Circle")
|
||||
{
|
||||
// Input was adapted — restore
|
||||
var savedInput = nd.InputConnectors[0];
|
||||
if (op.Input.Count > 0)
|
||||
{
|
||||
var inp = op.Input[0];
|
||||
inp.Shape = ParseShape(savedInput.Shape);
|
||||
inp.ConnectorColor = Color.FromArgb(savedInput.ColorArgb);
|
||||
inp.DataType = savedInput.DataType;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore output connectors
|
||||
var existingOutputs = op.Output.ToList();
|
||||
foreach (var eo in existingOutputs) op.Output.Remove(eo);
|
||||
foreach (var sc in nd.OutputConnectors)
|
||||
{
|
||||
op.Output.Add(DeserializeConnector(sc, false));
|
||||
}
|
||||
}
|
||||
else if (sysVm.SystemOperationType == SystemOperations.TAKE)
|
||||
{
|
||||
// Restore adapted list input
|
||||
foreach (var inp in op.Input)
|
||||
{
|
||||
if (!inp.IsTakeListConnector) continue;
|
||||
var savedInp = nd.InputConnectors.FirstOrDefault(c => c.IsTakeListConnector);
|
||||
if (savedInp != null)
|
||||
{
|
||||
inp.Shape = ParseShape(savedInp.Shape);
|
||||
inp.ConnectorColor = Color.FromArgb(savedInp.ColorArgb);
|
||||
inp.DataType = savedInp.DataType;
|
||||
}
|
||||
}
|
||||
// Restore output
|
||||
var existingOutputs = op.Output.ToList();
|
||||
foreach (var eo in existingOutputs) op.Output.Remove(eo);
|
||||
foreach (var sc in nd.OutputConnectors)
|
||||
{
|
||||
op.Output.Add(DeserializeConnector(sc, false));
|
||||
}
|
||||
}
|
||||
else if (sysVm.SystemOperationType == SystemOperations.AUTH)
|
||||
{
|
||||
if (op is AuthOperationViewModel authOp)
|
||||
{
|
||||
authOp.BaseUrl = nd.AuthBaseUrl ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(nd.AuthType))
|
||||
authOp.AuthType = nd.AuthType;
|
||||
|
||||
// Restore data input connectors (preserve the leading triangle flow connector).
|
||||
var dataInputs = op.Input.Where(i => i.Shape != ConnectorShape.Triangle).ToList();
|
||||
foreach (var d in dataInputs) op.Input.Remove(d);
|
||||
|
||||
foreach (var ic in nd.InputConnectors)
|
||||
{
|
||||
if (ic.Shape == "Triangle") continue;
|
||||
op.Input.Add(DeserializeConnector(ic, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sysVm.SystemOperationType == SystemOperations.KNOT)
|
||||
{
|
||||
// Restore knot connector shape/color/datatype from saved data
|
||||
if (op is KnotOperationViewModel knotOp)
|
||||
{
|
||||
knotOp.Input.Clear();
|
||||
knotOp.Output.Clear();
|
||||
|
||||
foreach (var ic in nd.InputConnectors)
|
||||
{
|
||||
var conn = DeserializeConnector(ic, true);
|
||||
conn.IsKnotConnector = true;
|
||||
knotOp.Input.Add(conn);
|
||||
}
|
||||
foreach (var oc in nd.OutputConnectors)
|
||||
{
|
||||
var conn = DeserializeConnector(oc, false);
|
||||
conn.IsKnotConnector = true;
|
||||
knotOp.Output.Add(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ConnectorData SerializeConnector(ConnectorViewModel c)
|
||||
{
|
||||
return new ConnectorData
|
||||
@@ -526,25 +197,5 @@ namespace Nodify.Calculator
|
||||
IsKnotConnector = c.IsKnotConnector
|
||||
};
|
||||
}
|
||||
|
||||
private static ConnectorViewModel DeserializeConnector(ConnectorData cd, bool isInput)
|
||||
{
|
||||
return new ConnectorViewModel
|
||||
{
|
||||
Title = cd.Title,
|
||||
Shape = ParseShape(cd.Shape),
|
||||
ConnectorColor = Color.FromArgb(cd.ColorArgb),
|
||||
DataType = cd.DataType,
|
||||
IsCopyConnector = cd.IsCopyConnector,
|
||||
IsTakeListConnector = cd.IsTakeListConnector,
|
||||
IsKnotConnector = cd.IsKnotConnector,
|
||||
IsInput = isInput
|
||||
};
|
||||
}
|
||||
|
||||
private static ConnectorShape ParseShape(string shape)
|
||||
{
|
||||
return Enum.TryParse<ConnectorShape>(shape, out var s) ? s : ConnectorShape.Circle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
145
Examples/Nodify.Calculator/NodeHandlers/BasicSystemHandlers.cs
Normal file
145
Examples/Nodify.Calculator/NodeHandlers/BasicSystemHandlers.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
// ─── BEGIN / END ───
|
||||
public class BeginEndHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System
|
||||
&& (info.sysOp == SystemOperations.BEGIN || info.sysOp == SystemOperations.END);
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System"
|
||||
&& (data.SystemOp == nameof(SystemOperations.BEGIN) || data.SystemOp == nameof(SystemOperations.END));
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = info.sysOp
|
||||
};
|
||||
if (info.sysOp == SystemOperations.BEGIN)
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", IsInput = false, Shape = ConnectorShape.Triangle, ConnectorColor = Color.DarkRed });
|
||||
else
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, ConnectorColor = Color.DarkRed });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var sysOp = data.SystemOp == nameof(SystemOperations.BEGIN) ? SystemOperations.BEGIN : SystemOperations.END;
|
||||
var info = new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = sysOp };
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
if (vm is SystemOperationViewModel sys)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = sys.SystemOperationType.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── COPY ───
|
||||
public class CopyHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.COPY;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.COPY);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title ?? "COPY",
|
||||
SystemOperationType = SystemOperations.COPY
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Circle, ConnectorColor = Color.White, IsCopyConnector = true });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var op = Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = SystemOperations.COPY });
|
||||
|
||||
// Restore adapted input connector
|
||||
if (data.InputConnectors.Count > 0 && data.InputConnectors[0].Shape != "Circle")
|
||||
{
|
||||
var saved = data.InputConnectors[0];
|
||||
if (op.Input.Count > 0)
|
||||
{
|
||||
var inp = op.Input[0];
|
||||
inp.Shape = NodeHandlerRegistry.ParseShape(saved.Shape);
|
||||
inp.ConnectorColor = Color.FromArgb(saved.ColorArgb);
|
||||
inp.DataType = saved.DataType;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore output connectors
|
||||
foreach (var sc in data.OutputConnectors)
|
||||
op.Output.Add(NodeHandlerRegistry.DeserializeConnector(sc, false));
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.COPY);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── SPLIT ───
|
||||
public class SplitHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.SPLIT;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.SPLIT);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title ?? "Split",
|
||||
SystemOperationType = SystemOperations.SPLIT
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Square, ConnectorColor = Color.MediumPurple });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var op = (SystemOperationViewModel)Create(new OperationInfoViewModel { Title = "Split", Type = OperationType.System, sysOp = SystemOperations.SPLIT });
|
||||
|
||||
// Restore dynamic outputs from saved data
|
||||
foreach (var sc in data.OutputConnectors)
|
||||
{
|
||||
if (sc.Shape == "Triangle") continue;
|
||||
op.Output.Add(NodeHandlerRegistry.DeserializeConnector(sc, false));
|
||||
}
|
||||
op.Title = data.Title;
|
||||
return op;
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.SPLIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
170
Examples/Nodify.Calculator/NodeHandlers/FlowNodeHandlers.cs
Normal file
170
Examples/Nodify.Calculator/NodeHandlers/FlowNodeHandlers.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
// ─── DEBUG ───
|
||||
public class DebugHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.DEBUG;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.DEBUG);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title ?? "Debug",
|
||||
SystemOperationType = SystemOperations.DEBUG
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
op.Input.Add(new ConnectorViewModel { Title = "Value", ConnectorColor = Color.LimeGreen });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
=> Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = SystemOperations.DEBUG });
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── AUTH ───
|
||||
public class AuthHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.AUTH;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.AUTH);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new AuthOperationViewModel();
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
// Data inputs
|
||||
var labels = new[] { "Base URL", "Auth Type", "Token", "API Key", "Username", "Password" };
|
||||
foreach (var label in labels)
|
||||
op.Input.Add(new ConnectorViewModel { Title = label, ConnectorColor = Color.Orange });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var op = new AuthOperationViewModel
|
||||
{
|
||||
BaseUrl = data.AuthBaseUrl ?? string.Empty,
|
||||
AuthType = !string.IsNullOrEmpty(data.AuthType) ? data.AuthType : "Bearer Token"
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
|
||||
// Restore data inputs from saved connectors (skip triangles)
|
||||
foreach (var ic in data.InputConnectors)
|
||||
{
|
||||
if (ic.Shape == "Triangle") continue;
|
||||
op.Input.Add(NodeHandlerRegistry.DeserializeConnector(ic, true));
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.AUTH);
|
||||
if (vm is AuthOperationViewModel auth)
|
||||
{
|
||||
data.AuthBaseUrl = auth.BaseUrl ?? string.Empty;
|
||||
data.AuthType = auth.AuthType ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── TAKE ───
|
||||
public class TakeHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.TAKE;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.TAKE);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new TakeOperationViewModel
|
||||
{
|
||||
Title = info.Title ?? "TAKE",
|
||||
SystemOperationType = SystemOperations.TAKE
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "List",
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = Color.White,
|
||||
IsCopyConnector = true,
|
||||
IsTakeListConnector = true
|
||||
});
|
||||
var nthConnector = new ConnectorViewModel
|
||||
{
|
||||
Title = "Nth",
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = ConnectorViewModel.GetColorForType("int"),
|
||||
DataType = "int"
|
||||
};
|
||||
op.NthConnector = nthConnector;
|
||||
op.Input.Add(nthConnector);
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var op = (TakeOperationViewModel)Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = SystemOperations.TAKE });
|
||||
op.NthIndex = data.NthIndex;
|
||||
op.IsRandom = data.IsRandom;
|
||||
|
||||
// Restore adapted list input
|
||||
var savedListInp = data.InputConnectors.FirstOrDefault(c => c.IsTakeListConnector);
|
||||
if (savedListInp != null)
|
||||
{
|
||||
var listInp = op.Input.FirstOrDefault(i => i.IsTakeListConnector);
|
||||
if (listInp != null)
|
||||
{
|
||||
listInp.Shape = NodeHandlerRegistry.ParseShape(savedListInp.Shape);
|
||||
listInp.ConnectorColor = Color.FromArgb(savedListInp.ColorArgb);
|
||||
listInp.DataType = savedListInp.DataType;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore outputs
|
||||
foreach (var sc in data.OutputConnectors)
|
||||
op.Output.Add(NodeHandlerRegistry.DeserializeConnector(sc, false));
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.TAKE);
|
||||
if (vm is TakeOperationViewModel take)
|
||||
{
|
||||
data.NthIndex = take.NthIndex;
|
||||
data.IsRandom = take.IsRandom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
209
Examples/Nodify.Calculator/NodeHandlers/GetSetHandlers.cs
Normal file
209
Examples/Nodify.Calculator/NodeHandlers/GetSetHandlers.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
// ─── GET/SET for simple variables (IsSimpleVariable = true) ───
|
||||
public class GetSetVariableHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System
|
||||
&& info.sysOp == SystemOperations.GET_SET
|
||||
&& info.IsSimpleVariable;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System"
|
||||
&& data.SystemOp == nameof(SystemOperations.GET_SET)
|
||||
&& data.IsSimpleVariable;
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var varType = (info.VariableType ?? "").ToLower();
|
||||
var varColor = ConnectorViewModel.GetColorForType(varType);
|
||||
var varLabel = $"{info.Title} {info.ClassName} ({info.VariableType})";
|
||||
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = varLabel,
|
||||
SystemOperationType = SystemOperations.GET_SET,
|
||||
IsSimpleVariable = true,
|
||||
VariableType = info.VariableType ?? string.Empty,
|
||||
ClassName = info.ClassName ?? string.Empty
|
||||
};
|
||||
|
||||
if (info.Title == "GET")
|
||||
{
|
||||
op.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Value",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = varColor,
|
||||
DataType = varType
|
||||
});
|
||||
}
|
||||
else // SET
|
||||
{
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
op.Input.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Value",
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = varColor,
|
||||
DataType = varType
|
||||
});
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var savedTitle = data.Title ?? "";
|
||||
var prefix = savedTitle.StartsWith("SET ") ? "SET" : "GET";
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = prefix,
|
||||
Type = OperationType.System,
|
||||
sysOp = SystemOperations.GET_SET,
|
||||
IsSimpleVariable = true,
|
||||
VariableType = data.VariableType ?? string.Empty,
|
||||
ClassName = data.ClassName ?? string.Empty
|
||||
};
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.GET_SET);
|
||||
if (vm is SystemOperationViewModel sys)
|
||||
{
|
||||
data.ClassName = sys.ClassName ?? string.Empty;
|
||||
data.IsSimpleVariable = true;
|
||||
data.VariableType = sys.VariableType ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── GET/SET for model types (IsModelNode = true, IsSimpleVariable = false) ───
|
||||
public class GetSetModelHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System
|
||||
&& info.sysOp == SystemOperations.GET_SET
|
||||
&& info.IsModelNode
|
||||
&& !info.IsSimpleVariable;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System"
|
||||
&& data.SystemOp == nameof(SystemOperations.GET_SET)
|
||||
&& !data.IsSimpleVariable
|
||||
&& !string.IsNullOrEmpty(data.ClassName);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = $"{info.Title} {info.ClassName}",
|
||||
SystemOperationType = SystemOperations.GET_SET,
|
||||
IsSimpleVariable = false,
|
||||
ClassName = info.ClassName ?? string.Empty
|
||||
};
|
||||
|
||||
if (info.Title == "GET")
|
||||
{
|
||||
// GET model: output a Square connector for the model
|
||||
op.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = info.ClassName ?? "",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Square,
|
||||
ConnectorColor = Color.MediumPurple,
|
||||
DataType = info.ClassName ?? "object"
|
||||
});
|
||||
}
|
||||
else // SET
|
||||
{
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
|
||||
// Try to read class properties for detailed connectors
|
||||
var customModelDir = Path.Combine(ProjectManager.ProjectDirectory, "CustomModels");
|
||||
Directory.CreateDirectory(customModelDir);
|
||||
var filePath = Path.Combine(customModelDir, info.ClassName + ".cs");
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var fileContent = File.ReadAllText(filePath);
|
||||
var properties = OperationFactory.GetPropertiesFromClassPublic(fileContent);
|
||||
|
||||
// Square input for the class object
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Square, ConnectorColor = Color.MediumPurple });
|
||||
|
||||
// Circle output per property, colored by type
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var propColor = ConnectorViewModel.GetColorForType(property.Type);
|
||||
op.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = $"{property.Name} ({property.Type})",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = propColor,
|
||||
DataType = property.Type.ToLower()
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: generic square I/O
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Square, ConnectorColor = Color.MediumPurple });
|
||||
op.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = info.ClassName ?? "",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Square,
|
||||
ConnectorColor = Color.MediumPurple,
|
||||
DataType = info.ClassName ?? "object"
|
||||
});
|
||||
}
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var savedTitle = data.Title ?? "";
|
||||
var prefix = savedTitle.StartsWith("SET ") ? "SET" : "GET";
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = prefix,
|
||||
Type = OperationType.System,
|
||||
sysOp = SystemOperations.GET_SET,
|
||||
IsModelNode = true,
|
||||
IsSimpleVariable = false,
|
||||
ClassName = data.ClassName ?? string.Empty
|
||||
};
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.GET_SET);
|
||||
if (vm is SystemOperationViewModel sys)
|
||||
{
|
||||
data.ClassName = sys.ClassName ?? string.Empty;
|
||||
data.IsSimpleVariable = false;
|
||||
data.VariableType = sys.VariableType ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Examples/Nodify.Calculator/NodeHandlers/INodeHandler.cs
Normal file
45
Examples/Nodify.Calculator/NodeHandlers/INodeHandler.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Nodify.Calculator.Models;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Each node type implements this interface to encapsulate its own
|
||||
/// creation, save and load logic — eliminating giant switch/if chains.
|
||||
/// </summary>
|
||||
public interface INodeHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// The NodeType discriminator stored in <see cref="NodeData.NodeType"/> (e.g. "System", "API", "Function").
|
||||
/// </summary>
|
||||
string NodeTypeKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this handler can create a node from the given <see cref="OperationInfoViewModel"/>.
|
||||
/// Used when building a node from the toolbox / left-panel.
|
||||
/// </summary>
|
||||
bool CanCreate(OperationInfoViewModel info);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this handler can restore a node from the given <see cref="NodeData"/>.
|
||||
/// Used when loading a saved graph.
|
||||
/// </summary>
|
||||
bool CanRestore(NodeData data);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="OperationViewModel"/> from toolbox info.
|
||||
/// </summary>
|
||||
OperationViewModel Create(OperationInfoViewModel info);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="OperationViewModel"/> from saved <see cref="NodeData"/>.
|
||||
/// The base properties (NodeId, Location) are set by the caller.
|
||||
/// </summary>
|
||||
OperationViewModel Restore(NodeData data);
|
||||
|
||||
/// <summary>
|
||||
/// Writes ViewModel-specific properties into <see cref="NodeData"/> during save.
|
||||
/// Common properties (NodeId, Title, Location, Connectors) are handled by the caller.
|
||||
/// </summary>
|
||||
void Save(OperationViewModel vm, NodeData data);
|
||||
}
|
||||
}
|
||||
133
Examples/Nodify.Calculator/NodeHandlers/NodeHandlerRegistry.cs
Normal file
133
Examples/Nodify.Calculator/NodeHandlers/NodeHandlerRegistry.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Registry that discovers and dispatches to the correct <see cref="INodeHandler"/>.
|
||||
/// Replaces all the switch/if chains in OperationFactory and GraphSerializer.
|
||||
/// </summary>
|
||||
public static class NodeHandlerRegistry
|
||||
{
|
||||
private static readonly List<INodeHandler> _handlers = new();
|
||||
private static bool _initialized;
|
||||
|
||||
public static IReadOnlyList<INodeHandler> Handlers
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _handlers;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
if (_initialized) return;
|
||||
_initialized = true;
|
||||
|
||||
// Register all handlers — order matters for CanCreate/CanRestore matching.
|
||||
// More specific handlers first (e.g. TakeHandler before generic SystemHandler).
|
||||
_handlers.Add(new KnotHandler());
|
||||
_handlers.Add(new FunctionHandler());
|
||||
_handlers.Add(new AuthHandler());
|
||||
_handlers.Add(new TakeHandler());
|
||||
_handlers.Add(new ForEachHandler());
|
||||
_handlers.Add(new AssertHandler());
|
||||
_handlers.Add(new DebugHandler());
|
||||
_handlers.Add(new NewObjectHandler());
|
||||
_handlers.Add(new CopyHandler());
|
||||
_handlers.Add(new SplitHandler());
|
||||
_handlers.Add(new GetSetVariableHandler());
|
||||
_handlers.Add(new GetSetModelHandler());
|
||||
_handlers.Add(new BeginEndHandler());
|
||||
_handlers.Add(new GenericSystemHandler());
|
||||
_handlers.Add(new ApiHandler());
|
||||
_handlers.Add(new ExpandoHandler());
|
||||
_handlers.Add(new NormalHandler());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the handler that can create a node from toolbox info.
|
||||
/// </summary>
|
||||
public static INodeHandler? FindForCreate(OperationInfoViewModel info)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _handlers.FirstOrDefault(h => h.CanCreate(info));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the handler that can restore a node from saved data.
|
||||
/// </summary>
|
||||
public static INodeHandler? FindForRestore(NodeData data)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _handlers.FirstOrDefault(h => h.CanRestore(data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the handler that can save a given ViewModel.
|
||||
/// </summary>
|
||||
public static INodeHandler? FindForSave(OperationViewModel vm)
|
||||
{
|
||||
EnsureInitialized();
|
||||
// Build a temporary info to test CanCreate? No — instead match by VM type.
|
||||
// We use a simple type check cascade, but kept in one place.
|
||||
return vm switch
|
||||
{
|
||||
KnotOperationViewModel => _handlers.OfType<KnotHandler>().FirstOrDefault(),
|
||||
FunctionOperationViewModel => _handlers.OfType<FunctionHandler>().FirstOrDefault(),
|
||||
AuthOperationViewModel => _handlers.OfType<AuthHandler>().FirstOrDefault(),
|
||||
TakeOperationViewModel => _handlers.OfType<TakeHandler>().FirstOrDefault(),
|
||||
ForEachOperationViewModel => _handlers.OfType<ForEachHandler>().FirstOrDefault(),
|
||||
AssertOperationViewModel => _handlers.OfType<AssertHandler>().FirstOrDefault(),
|
||||
APIOperationViewModel => _handlers.OfType<ApiHandler>().FirstOrDefault(),
|
||||
ExpandoOperationViewModel => _handlers.OfType<ExpandoHandler>().FirstOrDefault(),
|
||||
CalculatorOperationViewModel => _handlers.OfType<NormalHandler>().FirstOrDefault(),
|
||||
SystemOperationViewModel sys => FindSystemSaveHandler(sys),
|
||||
_ => _handlers.OfType<NormalHandler>().FirstOrDefault()
|
||||
};
|
||||
}
|
||||
|
||||
private static INodeHandler? FindSystemSaveHandler(SystemOperationViewModel sys)
|
||||
{
|
||||
return sys.SystemOperationType switch
|
||||
{
|
||||
SystemOperations.COPY => _handlers.OfType<CopyHandler>().FirstOrDefault(),
|
||||
SystemOperations.SPLIT => _handlers.OfType<SplitHandler>().FirstOrDefault(),
|
||||
SystemOperations.DEBUG => _handlers.OfType<DebugHandler>().FirstOrDefault(),
|
||||
SystemOperations.NEW_OBJECT => _handlers.OfType<NewObjectHandler>().FirstOrDefault(),
|
||||
SystemOperations.BEGIN => _handlers.OfType<BeginEndHandler>().FirstOrDefault(),
|
||||
SystemOperations.END => _handlers.OfType<BeginEndHandler>().FirstOrDefault(),
|
||||
SystemOperations.GET_SET when sys.IsSimpleVariable => _handlers.OfType<GetSetVariableHandler>().FirstOrDefault(),
|
||||
SystemOperations.GET_SET => _handlers.OfType<GetSetModelHandler>().FirstOrDefault(),
|
||||
_ => _handlers.OfType<GenericSystemHandler>().FirstOrDefault()
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Shared helpers used by multiple handlers ───
|
||||
|
||||
public static ConnectorViewModel DeserializeConnector(ConnectorData cd, bool isInput)
|
||||
{
|
||||
return new ConnectorViewModel
|
||||
{
|
||||
Title = cd.Title,
|
||||
Shape = ParseShape(cd.Shape),
|
||||
ConnectorColor = Color.FromArgb(cd.ColorArgb),
|
||||
DataType = cd.DataType,
|
||||
IsCopyConnector = cd.IsCopyConnector,
|
||||
IsTakeListConnector = cd.IsTakeListConnector,
|
||||
IsKnotConnector = cd.IsKnotConnector,
|
||||
IsInput = isInput
|
||||
};
|
||||
}
|
||||
|
||||
public static ConnectorShape ParseShape(string shape)
|
||||
{
|
||||
return Enum.TryParse<ConnectorShape>(shape, out var s) ? s : ConnectorShape.Circle;
|
||||
}
|
||||
}
|
||||
}
|
||||
293
Examples/Nodify.Calculator/NodeHandlers/OtherNodeHandlers.cs
Normal file
293
Examples/Nodify.Calculator/NodeHandlers/OtherNodeHandlers.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
// ─── FUNCTION ───
|
||||
public class FunctionHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "Function";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.FUNCTION;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "Function";
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new FunctionOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
FunctionName = info.Title ?? "Function"
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
op.ConfigureParameters(info.FunctionInputs, info.FunctionOutputs);
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = data.FunctionName,
|
||||
Type = OperationType.System,
|
||||
sysOp = SystemOperations.FUNCTION,
|
||||
IsFunction = true
|
||||
};
|
||||
foreach (var inp in data.FunctionInputs)
|
||||
info.FunctionInputs.Add(new FunctionParameterInfo { Name = inp.Name, Type = inp.Type });
|
||||
foreach (var outp in data.FunctionOutputs)
|
||||
info.FunctionOutputs.Add(new FunctionParameterInfo { Name = outp.Name, Type = outp.Type });
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "Function";
|
||||
if (vm is FunctionOperationViewModel func)
|
||||
{
|
||||
data.FunctionName = func.FunctionName;
|
||||
foreach (var p in func.InputParameters)
|
||||
data.FunctionInputs.Add(new FunctionParamData { Name = p.Name, Type = p.Type });
|
||||
foreach (var p in func.OutputParameters)
|
||||
data.FunctionOutputs.Add(new FunctionParamData { Name = p.Name, Type = p.Type });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── API ───
|
||||
public class ApiHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "API";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.API;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "API";
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new APIOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
OperationType = (info.OPType ?? "GET").ToUpper(),
|
||||
ResponseModelClassName = info.ResponseModelClassName ?? string.Empty
|
||||
};
|
||||
|
||||
// Flow connectors
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
|
||||
// Response output
|
||||
if (!string.IsNullOrEmpty(info.ResponseModelClassName))
|
||||
{
|
||||
var rc = info.ResponseModelClassName;
|
||||
bool isList = rc.StartsWith("List<") && rc.EndsWith(">");
|
||||
op.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = rc,
|
||||
IsInput = false,
|
||||
Shape = isList ? ConnectorShape.Grid : ConnectorShape.Square,
|
||||
ConnectorColor = isList ? Color.MediumSpringGreen : Color.MediumPurple,
|
||||
DataType = rc
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
op.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Response",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = Color.LimeGreen,
|
||||
DataType = "object"
|
||||
});
|
||||
}
|
||||
|
||||
// Data inputs
|
||||
foreach (var label in info.Input)
|
||||
{
|
||||
op.Input.Add(new ConnectorViewModel { Title = label, ConnectorColor = Color.LimeGreen });
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = data.Title,
|
||||
OPType = data.OPType?.ToLower() ?? "get",
|
||||
Type = OperationType.API,
|
||||
ResponseModelClassName = data.ResponseModelClassName ?? string.Empty
|
||||
};
|
||||
foreach (var ic in data.InputConnectors)
|
||||
{
|
||||
if (ic.Shape != "Triangle")
|
||||
info.Input.Add(ic.Title);
|
||||
}
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "API";
|
||||
if (vm is APIOperationViewModel api)
|
||||
{
|
||||
data.OPType = api.OperationType;
|
||||
data.ResponseModelClassName = api.ResponseModelClassName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── EXPANDO ───
|
||||
public class ExpandoHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "Expando";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.Expando;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "Expando";
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new ExpandoOperationViewModel
|
||||
{
|
||||
MaxInput = info.MaxInput,
|
||||
MinInput = info.MinInput,
|
||||
Title = info.Title,
|
||||
Operation = info.Operation
|
||||
};
|
||||
op.Output.Add(new ConnectorViewModel { ConnectorColor = Color.DodgerBlue });
|
||||
foreach (var label in info.Input)
|
||||
op.Input.Add(new ConnectorViewModel { Title = label, ConnectorColor = Color.DodgerBlue });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = data.Title,
|
||||
Type = OperationType.Expando
|
||||
};
|
||||
foreach (var ic in data.InputConnectors)
|
||||
info.Input.Add(ic.Title);
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "Expando";
|
||||
}
|
||||
}
|
||||
|
||||
// ─── NORMAL (default fallback) ───
|
||||
public class NormalHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "Normal";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.Normal
|
||||
|| info.Type == OperationType.Calculator
|
||||
|| info.Type == OperationType.Expression
|
||||
|| info.Type == OperationType.Group
|
||||
|| info.Type == OperationType.Graph;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "Normal" || data.NodeType == "Calculator";
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
// Delegate to old factory for Expression/Calculator/Group/Graph — these are rarely saved
|
||||
return OperationFactory.GetOperation(info);
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = data.Title,
|
||||
Type = data.NodeType == "Calculator" ? OperationType.Calculator : OperationType.Normal
|
||||
};
|
||||
foreach (var ic in data.InputConnectors)
|
||||
info.Input.Add(ic.Title);
|
||||
info.Output.Add("");
|
||||
return OperationFactory.GetOperation(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = vm is CalculatorOperationViewModel ? "Calculator" : "Normal";
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Catch-all for system nodes that don't have a specific handler ───
|
||||
public class GenericSystemHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System";
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
// Fallback: flow node with generic connectors
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = info.sysOp
|
||||
};
|
||||
if (info.IsFlowNode)
|
||||
{
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
}
|
||||
foreach (var label in info.Input)
|
||||
op.Input.Add(new ConnectorViewModel { Title = label, ConnectorColor = Color.Cyan });
|
||||
foreach (var label in info.Output)
|
||||
op.Output.Add(new ConnectorViewModel { Title = label, IsInput = false, ConnectorColor = Color.Cyan });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
if (!System.Enum.TryParse<SystemOperations>(data.SystemOp, out var sysOp))
|
||||
return Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System });
|
||||
|
||||
var info = new OperationInfoViewModel
|
||||
{
|
||||
Title = data.Title,
|
||||
Type = OperationType.System,
|
||||
sysOp = sysOp,
|
||||
IsFlowNode = true
|
||||
};
|
||||
foreach (var ic in data.InputConnectors)
|
||||
{
|
||||
if (ic.Shape != "Triangle") info.Input.Add(ic.Title);
|
||||
}
|
||||
foreach (var oc in data.OutputConnectors)
|
||||
{
|
||||
if (oc.Shape != "Triangle") info.Output.Add(oc.Title);
|
||||
}
|
||||
return Create(info);
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
if (vm is SystemOperationViewModel sys)
|
||||
data.SystemOp = sys.SystemOperationType.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
using Nodify.Calculator.Models;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nodify.Calculator.NodeHandlers
|
||||
{
|
||||
// ─── FOREACH ───
|
||||
public class ForEachHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.FOREACH;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.FOREACH);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new ForEachOperationViewModel { Title = info.Title ?? "ForEach" };
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
op.Input.Add(new ConnectorViewModel { Title = "List", Shape = ConnectorShape.Grid, ConnectorColor = Color.MediumSpringGreen });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "Loop Body", IsInput = false, Shape = ConnectorShape.Triangle, ConnectorColor = Color.MediumSpringGreen });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "Current Item", IsInput = false, Shape = ConnectorShape.Circle, ConnectorColor = Color.MediumSpringGreen, DataType = "object" });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "Index", IsInput = false, Shape = ConnectorShape.Circle, ConnectorColor = Color.LightSkyBlue, DataType = "int" });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
=> Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = SystemOperations.FOREACH });
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.FOREACH);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── ASSERT ───
|
||||
public class AssertHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.ASSERT;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.ASSERT);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new AssertOperationViewModel { Title = info.Title ?? "Assert" };
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
op.Input.Add(new ConnectorViewModel { Title = "Actual", ConnectorColor = Color.Gold, Shape = ConnectorShape.Circle });
|
||||
op.Input.Add(new ConnectorViewModel { Title = "Expected", ConnectorColor = Color.Gold, Shape = ConnectorShape.Circle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "Result", IsInput = false, Shape = ConnectorShape.Circle, ConnectorColor = Color.Gold, DataType = "bool" });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
=> Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = SystemOperations.ASSERT });
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.ASSERT);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── NEW_OBJECT ───
|
||||
public class NewObjectHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.NEW_OBJECT;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.NEW_OBJECT);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
{
|
||||
var op = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title ?? "New Object",
|
||||
SystemOperationType = SystemOperations.NEW_OBJECT
|
||||
};
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
op.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Square, ConnectorColor = Color.MediumPurple });
|
||||
op.Output.Add(new ConnectorViewModel { Title = "Object", IsInput = false, Shape = ConnectorShape.Square, ConnectorColor = Color.MediumPurple, DataType = "object" });
|
||||
return op;
|
||||
}
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var op = (SystemOperationViewModel)Create(new OperationInfoViewModel { Title = data.Title, Type = OperationType.System, sysOp = SystemOperations.NEW_OBJECT });
|
||||
|
||||
// Restore dynamic property inputs (keep triangle + square, replace the rest)
|
||||
var dynamicInputs = op.Input.Where(i => i.Shape != ConnectorShape.Triangle && i.Shape != ConnectorShape.Square).ToList();
|
||||
foreach (var d in dynamicInputs) op.Input.Remove(d);
|
||||
|
||||
foreach (var ic in data.InputConnectors)
|
||||
{
|
||||
if (ic.Shape == "Triangle" || ic.Shape == "Square") continue;
|
||||
op.Input.Add(NodeHandlerRegistry.DeserializeConnector(ic, true));
|
||||
}
|
||||
|
||||
// Restore output connector metadata (Square model output)
|
||||
var savedModelOutput = data.OutputConnectors.FirstOrDefault(o => o.Shape == "Square");
|
||||
var modelOutput = op.Output.FirstOrDefault(o => o.Shape == ConnectorShape.Square);
|
||||
if (savedModelOutput != null && modelOutput != null)
|
||||
{
|
||||
modelOutput.Title = savedModelOutput.Title;
|
||||
modelOutput.DataType = savedModelOutput.DataType;
|
||||
modelOutput.ConnectorColor = Color.FromArgb(savedModelOutput.ColorArgb);
|
||||
}
|
||||
|
||||
op.Title = data.Title;
|
||||
return op;
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.NEW_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── KNOT ───
|
||||
public class KnotHandler : INodeHandler
|
||||
{
|
||||
public string NodeTypeKey => "System";
|
||||
|
||||
public bool CanCreate(OperationInfoViewModel info)
|
||||
=> info.Type == OperationType.System && info.sysOp == SystemOperations.KNOT;
|
||||
|
||||
public bool CanRestore(NodeData data)
|
||||
=> data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.KNOT);
|
||||
|
||||
public OperationViewModel Create(OperationInfoViewModel info)
|
||||
=> new KnotOperationViewModel();
|
||||
|
||||
public OperationViewModel Restore(NodeData data)
|
||||
{
|
||||
var op = new KnotOperationViewModel();
|
||||
op.Input.Clear();
|
||||
op.Output.Clear();
|
||||
foreach (var ic in data.InputConnectors)
|
||||
{
|
||||
var conn = NodeHandlerRegistry.DeserializeConnector(ic, true);
|
||||
conn.IsKnotConnector = true;
|
||||
op.Input.Add(conn);
|
||||
}
|
||||
foreach (var oc in data.OutputConnectors)
|
||||
{
|
||||
var conn = NodeHandlerRegistry.DeserializeConnector(oc, false);
|
||||
conn.IsKnotConnector = true;
|
||||
op.Output.Add(conn);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public void Save(OperationViewModel vm, NodeData data)
|
||||
{
|
||||
data.NodeType = "System";
|
||||
data.SystemOp = nameof(SystemOperations.KNOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,6 +236,22 @@ namespace Nodify.Calculator
|
||||
}
|
||||
|
||||
public static OperationViewModel GetOperation(OperationInfoViewModel info)
|
||||
{
|
||||
// Route through the handler registry for all supported types
|
||||
var handler = NodeHandlers.NodeHandlerRegistry.FindForCreate(info);
|
||||
if (handler != null)
|
||||
{
|
||||
return handler.Create(info);
|
||||
}
|
||||
|
||||
// Fallback for legacy types not yet migrated
|
||||
return GetOperationLegacy(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy creation path — only used for Expression, Calculator, Group, Graph, and generic Normal types.
|
||||
/// </summary>
|
||||
internal static OperationViewModel GetOperationLegacy(OperationInfoViewModel info)
|
||||
{
|
||||
var input = info.Input.Select(i => new ConnectorViewModel
|
||||
{
|
||||
@@ -260,19 +276,6 @@ namespace Nodify.Calculator
|
||||
Operation = info.Operation,
|
||||
};
|
||||
|
||||
case OperationType.Expando:
|
||||
var o = new ExpandoOperationViewModel
|
||||
{
|
||||
MaxInput = info.MaxInput,
|
||||
MinInput = info.MinInput,
|
||||
Title = info.Title,
|
||||
Operation = info.Operation
|
||||
};
|
||||
o.Output.Add(new ConnectorViewModel { ConnectorColor = Color.DodgerBlue });
|
||||
foreach (var ei in input) { ei.ConnectorColor = Color.DodgerBlue; }
|
||||
o.Input.AddRange(input);
|
||||
return o;
|
||||
|
||||
case OperationType.Group:
|
||||
return new OperationGroupViewModel
|
||||
{
|
||||
@@ -286,476 +289,6 @@ namespace Nodify.Calculator
|
||||
DesiredSize = new Size(420, 250)
|
||||
};
|
||||
|
||||
case OperationType.API:
|
||||
var _o = new APIOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
OperationType = info.OPType.ToUpper(),
|
||||
ResponseModelClassName = info.ResponseModelClassName ?? string.Empty
|
||||
};
|
||||
var connectorViewModel = new ConnectorViewModel()
|
||||
{
|
||||
Title = "",
|
||||
Shape = ConnectorShape.Triangle
|
||||
};
|
||||
var connectorViewModel2 = new ConnectorViewModel()
|
||||
{
|
||||
Title = "",
|
||||
Shape = ConnectorShape.Triangle,
|
||||
IsInput = false
|
||||
};
|
||||
_o.Output.Add(connectorViewModel2);
|
||||
|
||||
// Add typed response output based on ResponseModelClassName
|
||||
if (!string.IsNullOrEmpty(info.ResponseModelClassName))
|
||||
{
|
||||
var responseClassName = info.ResponseModelClassName;
|
||||
bool isList = responseClassName.StartsWith("List<") && responseClassName.EndsWith(">");
|
||||
var innerType = isList ? responseClassName.Substring(5, responseClassName.Length - 6) : responseClassName;
|
||||
|
||||
// Use Grid shape for list/array types, Square for single model
|
||||
_o.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = responseClassName,
|
||||
IsInput = false,
|
||||
Shape = isList ? ConnectorShape.Grid : ConnectorShape.Square,
|
||||
ConnectorColor = isList ? Color.MediumSpringGreen : Color.MediumPurple,
|
||||
DataType = responseClassName
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default: generic object output
|
||||
_o.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Response",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = Color.LimeGreen,
|
||||
DataType = "object"
|
||||
});
|
||||
}
|
||||
|
||||
_o.Input.Add(connectorViewModel);
|
||||
foreach (var item in input)
|
||||
{
|
||||
item.ConnectorColor = Color.LimeGreen;
|
||||
_o.Input.Add(item);
|
||||
}
|
||||
return _o;
|
||||
case OperationType.System:
|
||||
if (info.sysOp == SystemOperations.FUNCTION)
|
||||
{
|
||||
var funcOp = new FunctionOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
FunctionName = info.Title
|
||||
};
|
||||
// Add flow connectors (triangle)
|
||||
var funcFlowIn = new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle };
|
||||
var funcFlowOut = new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false };
|
||||
funcOp.Input.Add(funcFlowIn);
|
||||
funcOp.Output.Add(funcFlowOut);
|
||||
|
||||
// Configure typed input/output parameters
|
||||
funcOp.ConfigureParameters(info.FunctionInputs, info.FunctionOutputs);
|
||||
return funcOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.NEW_OBJECT)
|
||||
{
|
||||
var newObjOp = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = SystemOperations.NEW_OBJECT
|
||||
};
|
||||
// Flow connectors
|
||||
newObjOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
newObjOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
// Model class input (Square — user connects a model source)
|
||||
foreach (var inp in input)
|
||||
{
|
||||
inp.Shape = ConnectorShape.Square;
|
||||
inp.ConnectorColor = Color.MediumPurple;
|
||||
newObjOp.Input.Add(inp);
|
||||
}
|
||||
// Model output (Square — returns the constructed object)
|
||||
newObjOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Object",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Square,
|
||||
ConnectorColor = Color.MediumPurple,
|
||||
DataType = "object"
|
||||
});
|
||||
return newObjOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.FOREACH)
|
||||
{
|
||||
var forEachOp = new ForEachOperationViewModel
|
||||
{
|
||||
Title = info.Title
|
||||
};
|
||||
// Flow connectors
|
||||
forEachOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
forEachOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
// List data input (Grid shape for array/list types)
|
||||
forEachOp.Input.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "List",
|
||||
Shape = ConnectorShape.Grid,
|
||||
ConnectorColor = Color.MediumSpringGreen
|
||||
});
|
||||
// "Loop Body" flow output — connects to nodes executed per iteration
|
||||
forEachOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Loop Body",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Triangle,
|
||||
ConnectorColor = Color.MediumSpringGreen
|
||||
});
|
||||
// "Current Item" data output — the element of the current iteration
|
||||
forEachOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Current Item",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = Color.MediumSpringGreen,
|
||||
DataType = "object"
|
||||
});
|
||||
// "Index" data output — current iteration index
|
||||
forEachOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Index",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = Color.LightSkyBlue,
|
||||
DataType = "int"
|
||||
});
|
||||
return forEachOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.ASSERT)
|
||||
{
|
||||
var assertOp = new AssertOperationViewModel
|
||||
{
|
||||
Title = info.Title
|
||||
};
|
||||
// Flow connectors
|
||||
assertOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
assertOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
// Data inputs
|
||||
foreach (var inp in input)
|
||||
{
|
||||
inp.ConnectorColor = Color.Gold;
|
||||
inp.Shape = ConnectorShape.Circle;
|
||||
assertOp.Input.Add(inp);
|
||||
}
|
||||
// "Result" output — pass/fail boolean
|
||||
assertOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Result",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = Color.Gold,
|
||||
DataType = "bool"
|
||||
});
|
||||
return assertOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.KNOT)
|
||||
{
|
||||
return new KnotOperationViewModel();
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.DEBUG)
|
||||
{
|
||||
var debugOp = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = SystemOperations.DEBUG
|
||||
};
|
||||
// Flow connectors
|
||||
debugOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
debugOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
// Data input
|
||||
foreach (var inp in input)
|
||||
{
|
||||
inp.ConnectorColor = Color.LimeGreen;
|
||||
debugOp.Input.Add(inp);
|
||||
}
|
||||
return debugOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.AUTH)
|
||||
{
|
||||
var authOp = new AuthOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = SystemOperations.AUTH
|
||||
};
|
||||
// Add flow connectors (triangle)
|
||||
var flowIn = new ConnectorViewModel()
|
||||
{
|
||||
Title = "",
|
||||
Shape = ConnectorShape.Triangle
|
||||
};
|
||||
var flowOut = new ConnectorViewModel()
|
||||
{
|
||||
Title = "",
|
||||
Shape = ConnectorShape.Triangle,
|
||||
IsInput = false
|
||||
};
|
||||
authOp.Input.Add(flowIn);
|
||||
authOp.Output.Add(flowOut);
|
||||
// Add data input connectors
|
||||
foreach (var inp in input)
|
||||
{
|
||||
inp.ConnectorColor = Color.Orange;
|
||||
authOp.Input.Add(inp);
|
||||
}
|
||||
return authOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.TAKE)
|
||||
{
|
||||
var takeOp = new TakeOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = SystemOperations.TAKE
|
||||
};
|
||||
|
||||
// List input — accepts any shape but only list/array DataTypes
|
||||
foreach (var item in input)
|
||||
{
|
||||
item.Shape = ConnectorShape.Circle;
|
||||
item.ConnectorColor = Color.White;
|
||||
item.IsCopyConnector = true;
|
||||
item.IsTakeListConnector = true;
|
||||
item.Title = "List";
|
||||
takeOp.Input.Add(item);
|
||||
}
|
||||
|
||||
// Nth index input (int type)
|
||||
var nthConnector = new ConnectorViewModel
|
||||
{
|
||||
Title = "Nth",
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = ConnectorViewModel.GetColorForType("int"),
|
||||
DataType = "int"
|
||||
};
|
||||
takeOp.NthConnector = nthConnector;
|
||||
takeOp.Input.Add(nthConnector);
|
||||
|
||||
return takeOp;
|
||||
}
|
||||
|
||||
var sysOp = new SystemOperationViewModel
|
||||
{
|
||||
Title = info.Title,
|
||||
SystemOperationType = info.sysOp,
|
||||
IsSimpleVariable = info.IsSimpleVariable,
|
||||
VariableType = info.VariableType ?? string.Empty,
|
||||
ClassName = info.ClassName ?? string.Empty
|
||||
};
|
||||
|
||||
if (info.sysOp == SystemOperations.COPY)
|
||||
{
|
||||
sysOp.Title = info.Title;
|
||||
// Universal input — accepts any shape. Starts with no outputs.
|
||||
foreach (var item in input)
|
||||
{
|
||||
item.Shape = ConnectorShape.Circle;
|
||||
item.ConnectorColor = Color.White;
|
||||
item.IsCopyConnector = true;
|
||||
sysOp.Input.Add(item);
|
||||
}
|
||||
return sysOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.SPLIT)
|
||||
{
|
||||
sysOp.Title = info.Title;
|
||||
// Square input to accept model/class objects
|
||||
foreach (var item in input)
|
||||
{
|
||||
item.Shape = ConnectorShape.Square;
|
||||
item.ConnectorColor = Color.MediumPurple;
|
||||
sysOp.Input.Add(item);
|
||||
}
|
||||
return sysOp;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.GET_SET && info.IsModelNode && !info.IsSimpleVariable)
|
||||
{
|
||||
if (info.Title == "GET")
|
||||
{
|
||||
info.Output.Add("");
|
||||
info.IsFlowNode = false;
|
||||
}
|
||||
else if (info.Title == "SET")
|
||||
{
|
||||
info.Input.Add("");
|
||||
var customModelDir = System.IO.Path.Combine(ProjectManager.ProjectDirectory, "CustomModels");
|
||||
Directory.CreateDirectory(customModelDir);
|
||||
var flpath = System.IO.Path.Combine(customModelDir, info.ClassName + ".cs");
|
||||
if (File.Exists(flpath))
|
||||
{
|
||||
var fileContent = File.ReadAllText(flpath);
|
||||
var properties = GetPropertiesFromClass(fileContent);
|
||||
|
||||
Console.WriteLine("Properties found:");
|
||||
foreach (var property in properties)
|
||||
{
|
||||
Console.WriteLine($"Property Name: {property.Name}, Type: {property.Type}");
|
||||
}
|
||||
|
||||
info.IsFlowNode = true;
|
||||
|
||||
// Build the node with flow connectors first
|
||||
var flTitle = $"{info.Title} {info.ClassName}";
|
||||
sysOp.Title = flTitle;
|
||||
|
||||
// Flow connectors
|
||||
sysOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
sysOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
|
||||
// Input: Square connector for the class object
|
||||
foreach (var item in input)
|
||||
{
|
||||
item.Shape = ConnectorShape.Square;
|
||||
item.ConnectorColor = Color.MediumPurple;
|
||||
sysOp.Input.Add(item);
|
||||
}
|
||||
|
||||
// Outputs: Circle connectors per property, colored by type
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var propColor = ConnectorViewModel.GetColorForType(property.Type);
|
||||
var propType = property.Type.ToLower();
|
||||
sysOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = $"{property.Name} ({property.Type})",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = propColor,
|
||||
DataType = propType
|
||||
});
|
||||
}
|
||||
|
||||
return sysOp;
|
||||
}
|
||||
|
||||
info.IsFlowNode = true;
|
||||
}
|
||||
var flTitle2 = $"{info.Title} {info.ClassName}";
|
||||
sysOp.Title = flTitle2;
|
||||
}
|
||||
|
||||
if (info.sysOp == SystemOperations.GET_SET && info.IsSimpleVariable)
|
||||
{
|
||||
var varLabel = $"{info.Title} {info.ClassName} ({info.VariableType})";
|
||||
sysOp.Title = varLabel;
|
||||
var varType = info.VariableType.ToLower();
|
||||
var varColor = ConnectorViewModel.GetColorForType(varType);
|
||||
|
||||
if (info.Title == "GET")
|
||||
{
|
||||
// GET variable: output the value, no flow needed
|
||||
info.IsFlowNode = false;
|
||||
|
||||
// Flow connectors not needed, output connector with type color
|
||||
sysOp.Output.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Value",
|
||||
IsInput = false,
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = varColor,
|
||||
DataType = varType
|
||||
});
|
||||
|
||||
return sysOp;
|
||||
}
|
||||
else if (info.Title == "SET")
|
||||
{
|
||||
// SET variable: input connector for the value, flow node
|
||||
info.IsFlowNode = true;
|
||||
|
||||
// Flow connectors
|
||||
sysOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||
sysOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||
|
||||
// Data input with type color
|
||||
sysOp.Input.Add(new ConnectorViewModel
|
||||
{
|
||||
Title = "Value",
|
||||
Shape = ConnectorShape.Circle,
|
||||
ConnectorColor = varColor,
|
||||
DataType = varType
|
||||
});
|
||||
|
||||
return sysOp;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.sysOp != SystemOperations.BEGIN &&
|
||||
info.sysOp != SystemOperations.END &&
|
||||
info.IsFlowNode)
|
||||
{
|
||||
var flowinputnode = new ConnectorViewModel()
|
||||
{
|
||||
Title = "",
|
||||
Shape = ConnectorShape.Triangle
|
||||
};
|
||||
var flowoutputnode = new ConnectorViewModel()
|
||||
{
|
||||
Title = "",
|
||||
Shape = ConnectorShape.Triangle,
|
||||
IsInput = false
|
||||
};
|
||||
sysOp.Input.Add(flowinputnode);
|
||||
sysOp.Output.Add(flowoutputnode);
|
||||
}
|
||||
// Determine connector shape & color based on system operation type
|
||||
ConnectorShape dataShape;
|
||||
System.Drawing.Color dataColor;
|
||||
if (info.IsModelNode && !info.IsSimpleVariable)
|
||||
{
|
||||
dataShape = ConnectorShape.Square;
|
||||
dataColor = Color.MediumPurple;
|
||||
}
|
||||
else if (info.IsSimpleVariable)
|
||||
{
|
||||
dataShape = ConnectorShape.Circle;
|
||||
dataColor = Color.LightGreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataShape = ConnectorShape.Circle;
|
||||
dataColor = Color.Cyan;
|
||||
}
|
||||
|
||||
foreach (var item in info.Output)
|
||||
{
|
||||
var isBeginEnd = info.sysOp == SystemOperations.BEGIN || info.sysOp == SystemOperations.END;
|
||||
var out1 = new ConnectorViewModel()
|
||||
{
|
||||
Title = string.IsNullOrEmpty(item) ? "" : item,
|
||||
IsInput = false,
|
||||
Shape = isBeginEnd ? ConnectorShape.Triangle : dataShape,
|
||||
ConnectorColor = isBeginEnd ? Color.DarkRed : dataColor,
|
||||
};
|
||||
sysOp.Output.Add(out1);
|
||||
}
|
||||
foreach (var item in input)
|
||||
{
|
||||
var isBeginEnd = info.sysOp == SystemOperations.BEGIN || info.sysOp == SystemOperations.END;
|
||||
item.Shape = isBeginEnd ? ConnectorShape.Triangle : dataShape;
|
||||
item.ConnectorColor = isBeginEnd ? Color.DarkRed : dataColor;
|
||||
sysOp.Input.Add(item);
|
||||
}
|
||||
return sysOp;
|
||||
default:
|
||||
{
|
||||
var op = new OperationViewModel
|
||||
|
||||
Reference in New Issue
Block a user