836 lines
40 KiB
C#
836 lines
40 KiB
C#
using System.Linq;
|
|
using System.Text;
|
|
using NodePipeline.Abstractions.Interfaces.Nodes;
|
|
using NodePipeline.Engine.CodeGeneration.Abstractions;
|
|
using NodePipeline.Engine.CodeGeneration.Abstractions.Models;
|
|
|
|
namespace NodePipeline.Engine.CodeGeneration;
|
|
|
|
internal static class NodeFactoryGenerator
|
|
{
|
|
public static string Generate(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("using System.Globalization;");
|
|
sb.AppendLine("using NodePipeline.Abstractions;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Exceptions.NodeFactory;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Interfaces;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Interfaces.Nodes;");
|
|
sb.AppendLine("using NodePipeline.Configuration.Abstractions.Models.Execute;");
|
|
sb.AppendLine("using NodePipeline.Engine.Abstractions;");
|
|
sb.AppendLine("using NodePipeline.Engine.Abstractions.Validation;");
|
|
sb.AppendLine("using NodePipeline.Engine.NodeFields;");
|
|
sb.AppendLine("using NodePipeline.Engine.Utils;");
|
|
sb.AppendLine();
|
|
sb.AppendLine("// ReSharper disable CheckNamespace");
|
|
sb.AppendLine("// ReSharper disable ConvertTypeCheckPatternToNullCheck");
|
|
sb.AppendLine("// ReSharper disable HeuristicUnreachableCode");
|
|
sb.AppendLine("// ReSharper disable RedundantCast");
|
|
sb.AppendLine("// ReSharper disable RedundantNameQualifier");
|
|
sb.AppendLine("// ReSharper disable ReturnTypeCanBeNotNullable\n");
|
|
sb.AppendLine("// ReSharper disable UnusedMember.Local");
|
|
sb.AppendLine("// ReSharper disable UnusedParameter.Local");
|
|
sb.AppendLine();
|
|
sb.AppendLine("#nullable enable");
|
|
sb.AppendLine("#pragma warning disable CS0162 // Unreachable code detected");
|
|
sb.AppendLine();
|
|
sb.AppendLine("namespace NodePipeline.Engine.Generated;");
|
|
sb.AppendLine();
|
|
sb.AppendLine("public sealed class GeneratedNodeFactory : INodeFactory");
|
|
sb.AppendLine("{");
|
|
|
|
sb.AppendLine(" public Dictionary<string, Func<INode>> NodeFactories { get; set; } = new();");
|
|
sb.AppendLine(
|
|
" public IPipelineLocalizationProvider PipelineLocalizationProvider { get; set; } = new PipelineLocalizationProvider();");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(BuildCreateNodeMethod(model));
|
|
sb.AppendLine(BuildInitializeNodeFieldsMethod(model));
|
|
sb.AppendLine(BuildInitializeNodeFieldsSpecificMethods(model));
|
|
sb.AppendLine(BuildGetParameterCodesMethod(model));
|
|
sb.AppendLine(BuildParameterCodesForMethods(model));
|
|
sb.AppendLine(BuildReadParameterValueMethod(model));
|
|
sb.AppendLine(BuildReadParameterValueForMethods(model));
|
|
sb.AppendLine(BuildGetNodePortsMethod(model));
|
|
sb.AppendLine(BuildGetNodePortsSpecificMethods(model));
|
|
sb.AppendLine(BuildGetNodeOutputsMethod(model));
|
|
sb.AppendLine(BuildGetNodeOutputsSpecificMethods(model));
|
|
sb.AppendLine(BuildConnectPortsMethod(model));
|
|
sb.AppendLine(BuildConnectPortsSpecificMethods(model));
|
|
sb.AppendLine(BuildConnectPortsStructGenericMethod());
|
|
sb.AppendLine(BuildConnectPortsClassGenericMethod());
|
|
sb.AppendLine(BuildParseParameterMethod(model));
|
|
sb.AppendLine(BuildGetParameterDefaultValueMethod(model));
|
|
sb.AppendLine(BuildGetParameterDefaultValueSpecificMethods(model));
|
|
sb.AppendLine(BuildSetNodeParametersValuesMethod(model));
|
|
sb.AppendLine(BuildSetNodeParametersValuesSpecificMethods(model));
|
|
sb.AppendLine(BuildGetNodeTypeMethod(model));
|
|
|
|
sb.AppendLine(
|
|
" private record PortConnectionInfo(bool Connected, object? OutputFieldObj, string FromPortName, string FromNodeType, string FromNodeId);");
|
|
sb.AppendLine("}");
|
|
sb.AppendLine("#pragma warning restore CS0162 // Unreachable code detected");
|
|
sb.AppendLine("#nullable restore");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildCreateNodeMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
//TODO: add method description
|
|
sb.AppendLine(" public INode CreateNode(string pipelineId, NodeConfig config)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" INode? node;");
|
|
sb.AppendLine(" if (NodeFactories.TryGetValue(config.Type, out var f))");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" node = f();");
|
|
sb.AppendLine(" InitializeNodeFields(pipelineId, node, config.Id);");
|
|
sb.AppendLine(" return node;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" switch (config.Type)");
|
|
sb.AppendLine(" {");
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" {NodeGeneratorHelper.GetNodeNameCaseString(n)}");
|
|
if (n.HasParameterlessConstructor)
|
|
{
|
|
sb.AppendLine($" node = new {n.TypeNameFull}();");
|
|
sb.AppendLine(" break;");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(
|
|
" throw new NodeCanNotBeInitializedWithoutFactoryException(pipelineId, config.Type, config.Id);");
|
|
}
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, config.Type, config.Id);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" InitializeNodeFields(pipelineId, node, config.Id);");
|
|
sb.AppendLine(
|
|
" return node ?? throw new NodeWasNotInitializedException(pipelineId, config.Type, config.Id);");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSpecificInitializeNodeFieldsMethodName(NodeDescriptor node)
|
|
{
|
|
return $"Initialize__{node.TypeNameShortSanitized}__NodeFields";
|
|
}
|
|
|
|
private static string BuildInitializeNodeFieldsMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private void InitializeNodeFields(string pipelineId, INode node, string nodeId)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (node)");
|
|
sb.AppendLine(" {");
|
|
var i = 1;
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" case {n.TypeNameFull} t{i}:");
|
|
sb.AppendLine($" {GetSpecificInitializeNodeFieldsMethodName(n)}(t{i}, nodeId);");
|
|
sb.AppendLine(" break;");
|
|
i++;
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildInitializeNodeFieldsSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private static void {GetSpecificInitializeNodeFieldsMethodName(n)}({n.TypeNameFull} node, string nodeId)");
|
|
sb.AppendLine(" {");
|
|
foreach (var f in n.Fields)
|
|
{
|
|
var dir = f.Direction switch
|
|
{
|
|
FieldDirection.Input => "FieldDirection.Input",
|
|
FieldDirection.Output => "FieldDirection.Output",
|
|
_ => "FieldDirection.Parameter"
|
|
};
|
|
|
|
var nodeFieldType = string.Concat(f.ValueType, f.IsNullableValueType ? "?" : string.Empty);
|
|
sb.AppendLine(
|
|
$" node.{f.PropertyName} = new NodePipeline.Abstractions.Models.NodeField<{nodeFieldType}>()");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine($" Name = \"{f.FieldName}\",");
|
|
sb.AppendLine(
|
|
$" Code = NodeFieldCodeGenerator.GenerateFieldCode(nodeId, \"{f.FieldName}\", {dir}),");
|
|
sb.AppendLine($" Direction = {dir},");
|
|
sb.AppendLine(" // Description intentionally omitted for runtime");
|
|
sb.AppendLine(" };");
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSpecificParameterCodesMethodName(NodeDescriptor node)
|
|
{
|
|
return $"GetParameterCodesFor__{node.TypeNameShortSanitized}__Node";
|
|
}
|
|
|
|
private static string BuildGetParameterCodesMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" public IEnumerable<string> GetParameterCodes(string nodeType)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (nodeType)");
|
|
sb.AppendLine(" {");
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" {NodeGeneratorHelper.GetNodeNameCaseString(n)}");
|
|
sb.AppendLine($" return {GetSpecificParameterCodesMethodName(n)}();");
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeTypeNotFoundException(nodeType);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildParameterCodesForMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" private IEnumerable<string> {GetSpecificParameterCodesMethodName(n)}()");
|
|
sb.AppendLine(" {");
|
|
var nodeParameters = n.Fields
|
|
.Where(f => f.Direction == FieldDirection.Parameter)
|
|
.ToList();
|
|
sb.Append(" return");
|
|
if (nodeParameters.Count == 0)
|
|
{
|
|
sb.AppendLine(" [];");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(" [");
|
|
foreach (var f in n.Fields.Where(f => f.Direction == FieldDirection.Parameter))
|
|
sb.AppendLine($" \"{f.FieldName}\",");
|
|
sb.AppendLine(" ];");
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSpecificReadParameterValueMethodName(NodeDescriptor node)
|
|
{
|
|
return $"ReadParameterValueFrom__{node.TypeNameShortSanitized}__Node";
|
|
}
|
|
|
|
private static string BuildReadParameterValueMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" public object? ReadParameterValue(string pipelineId, string nodeId, string nodeType, string parameterName, string? valueString)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (nodeType)");
|
|
sb.AppendLine(" {");
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" {NodeGeneratorHelper.GetNodeNameCaseString(n)}");
|
|
sb.AppendLine(
|
|
$" return {GetSpecificReadParameterValueMethodName(n)}(pipelineId, nodeType, nodeId, parameterName, valueString);");
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeTypeNotFoundException(nodeType);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildReadParameterValueForMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private static object? {GetSpecificReadParameterValueMethodName(n)}(string pipelineId, string nodeType, string nodeId, string parameterName, string? valueString)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (parameterName)");
|
|
sb.AppendLine(" {");
|
|
foreach (var p in n.Fields.Where(f => f.Direction == FieldDirection.Parameter))
|
|
{
|
|
sb.AppendLine($" case \"{p.FieldName}\":");
|
|
sb.AppendLine($" return ParseParameter<{p.ValueType}>(valueString);");
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(
|
|
" throw new NodeParameterNotFoundException(pipelineId, parameterName, nodeType, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSpecificNodePortsMethodName(NodeDescriptor node)
|
|
{
|
|
return $"Get__{node.TypeNameShortSanitized}__NodePorts";
|
|
}
|
|
|
|
private static string BuildGetNodePortsMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" public Dictionary<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, HashSet<string> nodeTypes, Dictionary<string, IEnumerable<string>> nodeList)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var result = new Dictionary<NodePortKey, NodePortInfo>();");
|
|
sb.AppendLine(" foreach (var nodeType in nodeTypes)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (!nodeList.TryGetValue(nodeType, out var ids)) continue;");
|
|
sb.AppendLine(" foreach (var nodeId in ids)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var ports = GetNodePorts(pipelineId, nodeType, nodeId);");
|
|
sb.AppendLine(" foreach (var kv in ports) result.Add(kv.Key, kv.Value);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" return result;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(
|
|
" public IEnumerable<NodePortInfo> GetNodeInputPorts(string pipelineId, string nodeType, string nodeId)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var ports = GetNodePorts(pipelineId, nodeType, nodeId);");
|
|
sb.AppendLine(" foreach (var kv in ports) if (kv.Key.Input) yield return kv.Value;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(
|
|
" private Dictionary<NodePortKey, NodePortInfo> GetNodePorts(string pipelineId, string nodeType, string nodeId)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (nodeType)");
|
|
sb.AppendLine(" {");
|
|
foreach (var n in model.Nodes)
|
|
sb.AppendLine(
|
|
$" {NodeGeneratorHelper.GetNodeNameCaseString(n)} return {GetSpecificNodePortsMethodName(n)}(nodeId);");
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, nodeType, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BoolToString(bool value)
|
|
{
|
|
return value.ToString().ToLower();
|
|
}
|
|
|
|
private static string BuildGetNodePortsSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private static Dictionary<NodePortKey, NodePortInfo> {GetSpecificNodePortsMethodName(n)}(string nodeId)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" return new Dictionary<NodePortKey, NodePortInfo>()");
|
|
sb.AppendLine(" {");
|
|
foreach (var f in n.Fields.Where(f => f.Direction is FieldDirection.Input or FieldDirection.Output))
|
|
{
|
|
var input = f.Direction == FieldDirection.Input ? "true" : "false";
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine($" new NodePortKey(nodeId, \"{f.FieldName}\", {input}),");
|
|
sb.AppendLine(
|
|
$" new NodePortInfo(\"{f.FieldName}\", typeof({f.ValueType.TrimEnd('?')}), {BoolToString(f.IsNullableValueType)}, {BoolToString(f.Metadata?.IsRequired == true)}, {BoolToString(f.Metadata?.DisallowNullableOutput == true)}, {BoolToString(f.Direction == FieldDirection.Input)})");
|
|
sb.AppendLine(" },");
|
|
}
|
|
|
|
sb.AppendLine(" };");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetGetNodeOutputsSpecificMethodName(NodeDescriptor node)
|
|
{
|
|
return $"Get__{node.TypeNameShortSanitized}__NodeOutputs";
|
|
}
|
|
|
|
private static string BuildGetNodeOutputsMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private IReadOnlyDictionary<string, object> GetNodeOutputs(string pipelineId, string nodeId, INode node)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (node)");
|
|
sb.AppendLine(" {");
|
|
var i = 1;
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" case {n.TypeNameFull} t{i}:");
|
|
sb.AppendLine($" return {GetGetNodeOutputsSpecificMethodName(n)}(nodeId, t{i});");
|
|
i++;
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetNodeOutputsSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private IReadOnlyDictionary<string, object> {GetGetNodeOutputsSpecificMethodName(n)}(string nodeId, {n.TypeNameFull} node)");
|
|
sb.AppendLine(" {");
|
|
var nodeOutputs = n.Fields
|
|
.Where(f => f.Direction == FieldDirection.Output)
|
|
.ToList();
|
|
sb.Append(" return new Dictionary<string, object>");
|
|
if (nodeOutputs.Count == 0)
|
|
{
|
|
sb.AppendLine("();");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine();
|
|
sb.AppendLine(" {");
|
|
foreach (var p in nodeOutputs)
|
|
sb.AppendLine(
|
|
$" {{ NodeFieldCodeGenerator.GenerateFieldCode(nodeId, \"{p.FieldName}\", FieldDirection.Output), node.{p.PropertyName} }},");
|
|
sb.AppendLine(" };");
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetConnectPortsMethodName(NodeDescriptor node)
|
|
{
|
|
return $"Connect__{node.TypeNameShortSanitized}__NodePorts";
|
|
}
|
|
|
|
private static string BuildConnectPortsMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" public void ConnectPorts<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, InputSource> inputs, IReadOnlyDictionary<string, INode> createdNodes) where TNode : INode");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (node)");
|
|
sb.AppendLine(" {");
|
|
var i = 1;
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" case {n.TypeNameFull} t{i}:");
|
|
sb.AppendLine(
|
|
$" {GetConnectPortsMethodName(n)}(pipelineId, t{i}, nodeId, inputs, createdNodes);");
|
|
sb.AppendLine(" break;");
|
|
i++;
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildConnectPortsSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private PortConnectionInfo? TryConnectPorts<T>(string pipelineId, string nodeId, string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool isRequired = false) where T : notnull");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (!inputs.TryGetValue(inputKey, out var connection))");
|
|
sb.AppendLine(
|
|
" return !isRequired ? null : throw new InputPortHasNoRequiredConnectionException(pipelineId, inputKey, nodeId, nodeType);");
|
|
sb.AppendLine(" var fromNodeId = connection.NodeId;");
|
|
sb.AppendLine(" var fromPortName = connection.OutputName;");
|
|
sb.AppendLine(
|
|
" if (nodeInput == null) throw new InputPortNotFoundException(pipelineId, inputKey, nodeId, nodeType);");
|
|
sb.AppendLine(
|
|
" if (!createdNodes.TryGetValue(fromNodeId, out var fromNode)) throw new OutputPortNodeNotFoundException(pipelineId, fromNodeId, nodeId);");
|
|
sb.AppendLine(" var outputs = GetNodeOutputs(pipelineId, fromNodeId, fromNode);");
|
|
sb.AppendLine(
|
|
" var outputFieldKey = NodeFieldCodeGenerator.GenerateFieldCode(fromNodeId, fromPortName, FieldDirection.Output);");
|
|
sb.AppendLine(
|
|
" if (!outputs.TryGetValue(outputFieldKey, out var outputFieldObj)) throw new OutputPortNotFoundException(pipelineId, fromPortName, fromNodeId, nodeId);");
|
|
sb.AppendLine(" var fromNodeType = GetNodeType(pipelineId, fromNodeId, fromNode);");
|
|
sb.AppendLine(" if (outputFieldObj is INodeField<T> outStrict && nodeInput is INodeField<T> inStrict)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" PortConnector.ConnectStatic(outStrict, inStrict);");
|
|
sb.AppendLine(
|
|
" return new PortConnectionInfo(true, outputFieldObj, fromPortName, fromNodeType, fromNodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" return new PortConnectionInfo(false, outputFieldObj, fromPortName, fromNodeType, fromNodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private void {GetConnectPortsMethodName(n)}(string pipelineId, {n.TypeNameFull} node, string nodeId, Dictionary<string, InputSource> inputs, IReadOnlyDictionary<string, INode> createdNodes)");
|
|
sb.AppendLine(" {");
|
|
foreach (var input in n.Fields.Where(f => f.Direction == FieldDirection.Input))
|
|
{
|
|
var methodName = input.IsValueReferenceType ? "ConnectPortsClass" : "ConnectPortsStruct";
|
|
sb.AppendLine(
|
|
$" {methodName}(pipelineId, nodeId, \"{n.TypeNameShort}\", node.{input.PropertyName}, nameof(node.{input.PropertyName}), inputs, \"{input.FieldName}\", createdNodes, disallowNullableOutput: {BoolToString(input.Metadata?.DisallowNullableOutput == true)}, isRequired: true);");
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildParseParameterMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private static T? ParseParameter<T>(string? valueString)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var type = typeof(T);");
|
|
sb.AppendLine(
|
|
" if (type == typeof(string)) return valueString is not null ? (T)(object)valueString : default;");
|
|
sb.AppendLine(" if (string.IsNullOrEmpty(valueString)) return default;");
|
|
sb.AppendLine(" if (type == typeof(int)) return (T)(object)int.Parse(valueString);");
|
|
sb.AppendLine(
|
|
" if (type == typeof(double)) return (T)(object)double.Parse(valueString, CultureInfo.InvariantCulture);");
|
|
sb.AppendLine(" if (type == typeof(bool)) return (T)(object)bool.Parse(valueString);");
|
|
var knownEnums = model.Nodes.SelectMany(q => q.Fields
|
|
.Where(x => x.Direction == FieldDirection.Parameter && x.IsValueEnum))
|
|
.Select(q => q.ValueType)
|
|
.Distinct();
|
|
foreach (var enumType in knownEnums)
|
|
sb.AppendLine(
|
|
$" if (type == typeof({enumType})) return (T)(object)Enum.Parse<{enumType}>(valueString);");
|
|
sb.AppendLine(" throw new NotSupportedException(type.Name);");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetGetParameterDefaultValueMethodName(NodeDescriptor node)
|
|
{
|
|
return $"GetParameterDefaultValueFor__{node.TypeNameShortSanitized}__Node";
|
|
}
|
|
|
|
private static string BuildGetParameterDefaultValueMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" public object? GetParameterDefaultValue(string nodeType, string parameterName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (nodeType)");
|
|
sb.AppendLine(" {");
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" {NodeGeneratorHelper.GetNodeNameCaseString(n)}");
|
|
sb.AppendLine($" return {GetGetParameterDefaultValueMethodName(n)}(parameterName);");
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeParameterNotFoundException(parameterName, nodeType);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetParameterDefaultValueSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
const string tab = " ";
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private static object? {GetGetParameterDefaultValueMethodName(n)}(string parameterName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (parameterName)");
|
|
sb.AppendLine(" {");
|
|
foreach (var p in n.Fields.Where(f => f.Direction == FieldDirection.Parameter))
|
|
{
|
|
sb.AppendLine($" case \"{p.FieldName}\":");
|
|
var parameterType = string.Concat(p.ValueType, p.IsNullableValueType ? "?" : string.Empty);
|
|
sb.AppendLine(p.Metadata?.DefaultValue is null
|
|
? $" return GetDefaultValue<{parameterType}>();"
|
|
: GetDefaultValueCode(p, parameterType, tab));
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine($" throw new NodeParameterNotFoundException(parameterName, \"{n.Type}\");");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
sb.AppendLine(" private static T? GetDefaultValue<T>()");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var type = typeof(T);");
|
|
sb.AppendLine(" if (type == typeof(string)) return (T)(object)string.Empty;");
|
|
sb.AppendLine(" if (type == typeof(int)) return (T)(object)0;");
|
|
sb.AppendLine(" if (type == typeof(double)) return (T)(object)0d;");
|
|
sb.AppendLine(" if (type == typeof(decimal)) return (T)(object)0m;");
|
|
sb.AppendLine(" if (type == typeof(bool)) return (T)(object)false;");
|
|
sb.AppendLine(" if (type == typeof(Guid)) return (T)(object)Guid.Empty;");
|
|
sb.AppendLine(" if (type.IsEnum) return default;");
|
|
sb.AppendLine(" throw new NotSupportedException(type.Name);");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetDefaultValueCode(NodeFieldDescriptor parameterDescriptor, string parameterType,
|
|
string tabs)
|
|
{
|
|
var sb = new StringBuilder();
|
|
if (parameterDescriptor.Metadata?.DefaultValue != null)
|
|
{
|
|
var defaultValue = parameterDescriptor.Metadata.DefaultValue;
|
|
sb.Append(tabs);
|
|
switch (defaultValue)
|
|
{
|
|
case string strVal:
|
|
sb.Append($"return \"{strVal}\";");
|
|
break;
|
|
case char c:
|
|
sb.Append($"return '{c}';");
|
|
break;
|
|
case bool b:
|
|
sb.Append($"return {b.ToString().ToLowerInvariant()};");
|
|
break;
|
|
case int or long or short or byte or uint or ulong or ushort or sbyte:
|
|
sb.Append($"return {defaultValue};");
|
|
break;
|
|
case float f:
|
|
sb.Append($"return {f}f;");
|
|
break;
|
|
case double d:
|
|
sb.Append($"return {d}d;");
|
|
break;
|
|
case decimal dec:
|
|
sb.Append($"return {dec}m;");
|
|
break;
|
|
case null:
|
|
sb.Append("return null;");
|
|
break;
|
|
default:
|
|
sb.Append(parameterDescriptor.Metadata?.CanDefaultValueBeInitialized == true
|
|
? $"return new {parameterType}();"
|
|
: $"throw new NotSupportedException(\"Cannot initialize default value for type {parameterType}\");");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sb.Append($"return GetDefaultValue<{parameterType}>();");
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSetNodeParametersValuesMethodName(NodeDescriptor node)
|
|
{
|
|
return $"Set__{node.TypeNameShortSanitized}__NodeParametersValues";
|
|
}
|
|
|
|
private static string BuildSetNodeParametersValuesMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" public void SetNodeParametersValues<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, object?> parameters) where TNode : INode");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (node)");
|
|
sb.AppendLine(" {");
|
|
var i = 1;
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine($" case {n.TypeNameFull} t{i}:");
|
|
sb.AppendLine($" {GetSetNodeParametersValuesMethodName(n)}(t{i}, nodeId, parameters);");
|
|
sb.AppendLine(" break;");
|
|
i++;
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildSetNodeParametersValuesSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
sb.AppendLine(
|
|
$" private static void {GetSetNodeParametersValuesMethodName(n)}({n.TypeNameFull} node, string nodeId, Dictionary<string, object?> parameters)");
|
|
sb.AppendLine(" {");
|
|
var i = 1;
|
|
foreach (var p in n.Fields.Where(q => q.Direction == FieldDirection.Parameter))
|
|
{
|
|
sb.AppendLine(
|
|
$" if (parameters.TryGetValue(\"{p.FieldName}\", out var val{i}) && val{i} is {p.ValueType} typedVal{i})");
|
|
sb.AppendLine($" node.{p.PropertyName}.Value = typedVal{i};");
|
|
i++;
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
|
|
private static string BuildConnectPortsStructGenericMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private void ConnectPortsStruct<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs,");
|
|
sb.AppendLine(
|
|
" string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false)");
|
|
sb.AppendLine(" where T : struct");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" var portConnectionInfo = TryConnectPorts(pipelineId, nodeId, nodeType, nodeInput, nodeInputName, inputs, inputKey, createdNodes, isRequired);");
|
|
sb.AppendLine(" if (portConnectionInfo is null || portConnectionInfo.Connected) return;");
|
|
sb.AppendLine(
|
|
" if (typeof(T).IsValueType && portConnectionInfo.OutputFieldObj is INodeField<T?> outputField)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" PortConnector.ConnectRuntimeFromNullableStruct(outputField, nodeInput!,");
|
|
sb.AppendLine(
|
|
" nodeId, inputKey, portConnectionInfo.FromNodeId, portConnectionInfo.FromPortName);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNullable && nodeInput is INodeField<T?> inNullable)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" PortConnector.ConnectRuntimeBothNullable(outNullable, inNullable);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNToStrict && nodeInput != null)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (disallowNullableOutput)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" throw new NonNullableInputRestrictsConnectionToNullableOutputException(pipelineId, nodeId, nodeType, inputKey, portConnectionInfo.FromPortName, portConnectionInfo.FromNodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" PortConnector.ConnectRuntimeFromNullableStruct(outNToStrict, nodeInput, currentNodeId: nodeId,");
|
|
sb.AppendLine(
|
|
" inputPortName: inputKey, sourceNodeId: portConnectionInfo.FromNodeId, outputPortName: portConnectionInfo.FromPortName);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" if (portConnectionInfo.OutputFieldObj is INodeField<T> outStrictToN && nodeInput is INodeField<T?> inNullable2)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" PortConnector.ConnectRuntimeToNullableStruct(outStrictToN, inNullable2);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" throw new PortsTypeMismatchException(pipelineId, inputKey, nodeType, nodeId,");
|
|
sb.AppendLine(
|
|
" portConnectionInfo.FromPortName, portConnectionInfo.FromNodeType, portConnectionInfo.FromNodeId);");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildConnectPortsClassGenericMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private void ConnectPortsClass<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs,");
|
|
sb.AppendLine(
|
|
" string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false)");
|
|
sb.AppendLine(" where T : class");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" var portConnectionInfo = TryConnectPorts(pipelineId, nodeId, nodeType, nodeInput, nodeInputName, inputs, inputKey, createdNodes, isRequired);");
|
|
sb.AppendLine(" if (portConnectionInfo is null || portConnectionInfo.Connected) return;");
|
|
sb.AppendLine(
|
|
" if (!typeof(T).IsValueType && portConnectionInfo.OutputFieldObj is INodeField<T?> outputField)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" PortConnector.ConnectRuntimeFromNullableClass(outputField, nodeInput!,");
|
|
sb.AppendLine(
|
|
" nodeId, inputKey, portConnectionInfo.FromNodeId, portConnectionInfo.FromPortName);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNullable && nodeInput is INodeField<T?> inNullable)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" PortConnector.ConnectRuntimeBothNullable(outNullable, (INodeField<T?>)inNullable);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNToStrict && nodeInput != null)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (disallowNullableOutput)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" throw new NonNullableInputRestrictsConnectionToNullableOutputException(pipelineId, nodeId, nodeType, inputKey, portConnectionInfo.FromPortName, portConnectionInfo.FromNodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" PortConnector.ConnectRuntimeFromNullableClass(outNToStrict, nodeInput, currentNodeId: nodeId,");
|
|
sb.AppendLine(
|
|
" inputPortName: inputKey, sourceNodeId: portConnectionInfo.FromNodeId, outputPortName: portConnectionInfo.FromPortName);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(
|
|
" if (portConnectionInfo.OutputFieldObj is INodeField<T> outStrictToN && nodeInput is INodeField<T?> inNullable2)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" PortConnector.ConnectRuntimeToNullableClass(outStrictToN, (INodeField<T?>)inNullable2);");
|
|
sb.AppendLine(" return;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" throw new PortsTypeMismatchException(pipelineId, inputKey, nodeType, nodeId,");
|
|
sb.AppendLine(
|
|
" portConnectionInfo.FromPortName, portConnectionInfo.FromNodeType, portConnectionInfo.FromNodeId);");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetNodeTypeMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private static string GetNodeType(string pipelineId, string nodeId, INode node)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" switch (node)");
|
|
sb.AppendLine(" {");
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
var nodeName = NodeGeneratorHelper.GetNodeName(n);
|
|
sb.AppendLine($" case {n.TypeNameFull}:");
|
|
sb.AppendLine($" return {nodeName};");
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
return sb.ToString();
|
|
}
|
|
} |