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.Models;
|
||||||
|
using Nodify.Calculator.NodeHandlers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
@@ -34,54 +35,9 @@ namespace Nodify.Calculator
|
|||||||
LocationY = op.Location.Y
|
LocationY = op.Location.Y
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (op)
|
// Delegate to the handler for VM-specific properties
|
||||||
{
|
var handler = NodeHandlerRegistry.FindForSave(op);
|
||||||
case TakeOperationViewModel take:
|
handler?.Save(op, nodeData);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save connector metadata for all nodes
|
// Save connector metadata for all nodes
|
||||||
foreach (var c in op.Input)
|
foreach (var c in op.Input)
|
||||||
@@ -179,16 +135,17 @@ namespace Nodify.Calculator
|
|||||||
calculator.IsLoading = true;
|
calculator.IsLoading = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
var nodeMap = new Dictionary<string, OperationViewModel>();
|
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)
|
foreach (var nd in graph.Nodes)
|
||||||
{
|
{
|
||||||
OperationViewModel op = null;
|
OperationViewModel op = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
op = RecreateNode(nd, opsMenu);
|
var handler = NodeHandlerRegistry.FindForRestore(nd);
|
||||||
|
if (handler != null)
|
||||||
|
op = handler.Restore(nd);
|
||||||
}
|
}
|
||||||
catch
|
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)
|
private static ConnectorData SerializeConnector(ConnectorViewModel c)
|
||||||
{
|
{
|
||||||
return new ConnectorData
|
return new ConnectorData
|
||||||
@@ -526,25 +197,5 @@ namespace Nodify.Calculator
|
|||||||
IsKnotConnector = c.IsKnotConnector
|
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)
|
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
|
var input = info.Input.Select(i => new ConnectorViewModel
|
||||||
{
|
{
|
||||||
@@ -260,19 +276,6 @@ namespace Nodify.Calculator
|
|||||||
Operation = info.Operation,
|
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:
|
case OperationType.Group:
|
||||||
return new OperationGroupViewModel
|
return new OperationGroupViewModel
|
||||||
{
|
{
|
||||||
@@ -286,476 +289,6 @@ namespace Nodify.Calculator
|
|||||||
DesiredSize = new Size(420, 250)
|
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:
|
default:
|
||||||
{
|
{
|
||||||
var op = new OperationViewModel
|
var op = new OperationViewModel
|
||||||
|
|||||||
Reference in New Issue
Block a user