278 lines
12 KiB
C#
278 lines
12 KiB
C#
using System.Globalization;
|
|
using NodePipeline.Abstractions;
|
|
using NodePipeline.Abstractions.Exceptions.NodeFactory;
|
|
using NodePipeline.Abstractions.Interfaces;
|
|
using NodePipeline.Abstractions.Interfaces.Nodes;
|
|
using NodePipeline.Configuration.Abstractions.Models.Execute;
|
|
using NodePipeline.Engine.Abstractions;
|
|
using NodePipeline.Engine.NodeFields;
|
|
using NodePipeline.Engine.Utils;
|
|
using NodePipeline.Engine.Abstractions.Validation;
|
|
|
|
// ReSharper disable CheckNamespace
|
|
// ReSharper disable ConvertTypeCheckPatternToNullCheck
|
|
// ReSharper disable RedundantCast
|
|
// ReSharper disable RedundantNameQualifier
|
|
// ReSharper disable ReturnTypeCanBeNotNullable
|
|
|
|
// ReSharper disable UnusedMember.Local
|
|
// ReSharper disable UnusedParameter.Local
|
|
#nullable enable
|
|
|
|
namespace NodePipeline.Engine.Generated;
|
|
|
|
public sealed class GeneratedNodeFactory : INodeFactory
|
|
{
|
|
public Dictionary<string, Func<INode>> NodeFactories { get; set; } = new();
|
|
public IPipelineLocalizationProvider PipelineLocalizationProvider { get; set; } = new PipelineLocalizationProvider();
|
|
|
|
public INode CreateNode(string pipelineId, NodeConfig config)
|
|
{
|
|
INode? node;
|
|
if (NodeFactories.TryGetValue(config.Type, out var f))
|
|
{
|
|
node = f();
|
|
InitializeNodeFields(pipelineId, node, config.Id);
|
|
return node;
|
|
}
|
|
|
|
switch (config.Type)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, config.Type, config.Id);
|
|
}
|
|
InitializeNodeFields(pipelineId, node, config.Id);
|
|
return node ?? throw new NodeWasNotInitializedException(pipelineId, config.Type, config.Id);
|
|
}
|
|
|
|
private void InitializeNodeFields(string pipelineId, INode node, string nodeId)
|
|
{
|
|
switch (node)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);
|
|
}
|
|
}
|
|
|
|
|
|
public IEnumerable<string> GetParameterCodes(string nodeType)
|
|
{
|
|
switch (nodeType)
|
|
{
|
|
default:
|
|
throw new NodeTypeNotFoundException(nodeType);
|
|
}
|
|
}
|
|
|
|
|
|
public object? ReadParameterValue(string pipelineId, string nodeId, string nodeType, string parameterName, string? valueString)
|
|
{
|
|
switch (nodeType)
|
|
{
|
|
default:
|
|
throw new NodeTypeNotFoundException(nodeType);
|
|
}
|
|
}
|
|
|
|
|
|
public Dictionary<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, HashSet<string> nodeTypes, Dictionary<string, IEnumerable<string>> nodeList)
|
|
{
|
|
var result = new Dictionary<NodePortKey, NodePortInfo>();
|
|
foreach (var nodeType in nodeTypes)
|
|
{
|
|
if (!nodeList.TryGetValue(nodeType, out var ids)) continue;
|
|
foreach (var nodeId in ids)
|
|
{
|
|
var ports = GetNodePorts(pipelineId, nodeType, nodeId);
|
|
foreach (var kv in ports) result.Add(kv.Key, kv.Value);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public IEnumerable<NodePortInfo> GetNodeInputPorts(string pipelineId, string nodeType, string nodeId)
|
|
{
|
|
var ports = GetNodePorts(pipelineId, nodeType, nodeId);
|
|
foreach (var kv in ports) if (kv.Key.Input) yield return kv.Value;
|
|
}
|
|
|
|
private Dictionary<NodePortKey, NodePortInfo> GetNodePorts(string pipelineId, string nodeType, string nodeId)
|
|
{
|
|
switch (nodeType)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, nodeType, nodeId);
|
|
}
|
|
}
|
|
|
|
|
|
private IReadOnlyDictionary<string, object> GetNodeOutputs(string pipelineId, string nodeId, INode node)
|
|
{
|
|
switch (node)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public void ConnectPorts<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, InputSource> inputs, IReadOnlyDictionary<string, INode> createdNodes) where TNode : INode
|
|
{
|
|
switch (node)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
if (!inputs.TryGetValue(inputKey, out var connection))
|
|
return !isRequired ? null : throw new InputPortHasNoRequiredConnectionException(pipelineId, inputKey, nodeId, nodeType);
|
|
var fromNodeId = connection.NodeId;
|
|
var fromPortName = connection.OutputName;
|
|
if (nodeInput == null) throw new InputPortNotFoundException(pipelineId, inputKey, nodeId, nodeType);
|
|
if (!createdNodes.TryGetValue(fromNodeId, out var fromNode)) throw new OutputPortNodeNotFoundException(pipelineId, fromNodeId, nodeId);
|
|
var outputs = GetNodeOutputs(pipelineId, fromNodeId, fromNode);
|
|
var outputFieldKey = NodeFieldCodeGenerator.GenerateFieldCode(fromNodeId, fromPortName, FieldDirection.Output);
|
|
if (!outputs.TryGetValue(outputFieldKey, out var outputFieldObj)) throw new OutputPortNotFoundException(pipelineId, fromPortName, fromNodeId, nodeId);
|
|
var fromNodeType = GetNodeType(pipelineId, fromNodeId, fromNode);
|
|
if (outputFieldObj is INodeField<T> outStrict && nodeInput is INodeField<T> inStrict)
|
|
{
|
|
PortConnector.ConnectStatic(outStrict, inStrict);
|
|
return new PortConnectionInfo(true, outputFieldObj, fromPortName, fromNodeType, fromNodeId);
|
|
}
|
|
return new PortConnectionInfo(false, outputFieldObj, fromPortName, fromNodeType, fromNodeId);
|
|
}
|
|
|
|
|
|
private void ConnectPortsStruct<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs,
|
|
string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false)
|
|
where T : struct
|
|
{
|
|
var portConnectionInfo = TryConnectPorts(pipelineId, nodeId, nodeType, nodeInput, nodeInputName, inputs, inputKey, createdNodes, isRequired);
|
|
if (portConnectionInfo is null || portConnectionInfo.Connected) return;
|
|
if (typeof(T).IsValueType && portConnectionInfo.OutputFieldObj is INodeField<T?> outputField)
|
|
{
|
|
PortConnector.ConnectRuntimeFromNullableStruct(outputField, nodeInput!,
|
|
nodeId, inputKey, portConnectionInfo.FromNodeId, portConnectionInfo.FromPortName);
|
|
return;
|
|
}
|
|
if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNullable && nodeInput is INodeField<T?> inNullable)
|
|
{
|
|
PortConnector.ConnectRuntimeBothNullable(outNullable, inNullable);
|
|
return;
|
|
}
|
|
if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNToStrict && nodeInput != null)
|
|
{
|
|
if (disallowNullableOutput)
|
|
{
|
|
throw new NonNullableInputRestrictsConnectionToNullableOutputException(pipelineId, nodeId, nodeType, inputKey, portConnectionInfo.FromPortName, portConnectionInfo.FromNodeId);
|
|
}
|
|
PortConnector.ConnectRuntimeFromNullableStruct(outNToStrict, nodeInput, currentNodeId: nodeId,
|
|
inputPortName: inputKey, sourceNodeId: portConnectionInfo.FromNodeId, outputPortName: portConnectionInfo.FromPortName);
|
|
return;
|
|
}
|
|
if (portConnectionInfo.OutputFieldObj is INodeField<T> outStrictToN && nodeInput is INodeField<T?> inNullable2)
|
|
{
|
|
PortConnector.ConnectRuntimeToNullableStruct(outStrictToN, inNullable2);
|
|
return;
|
|
}
|
|
throw new PortsTypeMismatchException(pipelineId, inputKey, nodeType, nodeId,
|
|
portConnectionInfo.FromPortName, portConnectionInfo.FromNodeType, portConnectionInfo.FromNodeId);
|
|
}
|
|
|
|
private void ConnectPortsClass<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs,
|
|
string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false)
|
|
where T : class
|
|
{
|
|
var portConnectionInfo = TryConnectPorts(pipelineId, nodeId, nodeType, nodeInput, nodeInputName, inputs, inputKey, createdNodes, isRequired);
|
|
if (portConnectionInfo is null || portConnectionInfo.Connected) return;
|
|
if (!typeof(T).IsValueType && portConnectionInfo.OutputFieldObj is INodeField<T?> outputField)
|
|
{
|
|
PortConnector.ConnectRuntimeFromNullableClass(outputField, nodeInput!,
|
|
nodeId, inputKey, portConnectionInfo.FromNodeId, portConnectionInfo.FromPortName);
|
|
return;
|
|
}
|
|
if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNullable && nodeInput is INodeField<T?> inNullable)
|
|
{
|
|
PortConnector.ConnectRuntimeBothNullable(outNullable, (INodeField<T?>)inNullable);
|
|
return;
|
|
}
|
|
if (portConnectionInfo.OutputFieldObj is INodeField<T?> outNToStrict && nodeInput != null)
|
|
{
|
|
if (disallowNullableOutput)
|
|
{
|
|
throw new NonNullableInputRestrictsConnectionToNullableOutputException(pipelineId, nodeId, nodeType, inputKey, portConnectionInfo.FromPortName, portConnectionInfo.FromNodeId);
|
|
}
|
|
PortConnector.ConnectRuntimeFromNullableClass(outNToStrict, nodeInput, currentNodeId: nodeId,
|
|
inputPortName: inputKey, sourceNodeId: portConnectionInfo.FromNodeId, outputPortName: portConnectionInfo.FromPortName);
|
|
return;
|
|
}
|
|
if (portConnectionInfo.OutputFieldObj is INodeField<T> outStrictToN && nodeInput is INodeField<T?> inNullable2)
|
|
{
|
|
PortConnector.ConnectRuntimeToNullableClass(outStrictToN, (INodeField<T?>)inNullable2);
|
|
return;
|
|
}
|
|
throw new PortsTypeMismatchException(pipelineId, inputKey, nodeType, nodeId,
|
|
portConnectionInfo.FromPortName, portConnectionInfo.FromNodeType, portConnectionInfo.FromNodeId);
|
|
}
|
|
|
|
private static T? ParseParameter<T>(string? valueString)
|
|
{
|
|
var type = typeof(T);
|
|
if (type == typeof(string)) return valueString is not null ? (T)(object)valueString : default;
|
|
if (string.IsNullOrEmpty(valueString)) return default;
|
|
if (type == typeof(int)) return (T)(object)int.Parse(valueString);
|
|
if (type == typeof(double)) return (T)(object)double.Parse(valueString, CultureInfo.InvariantCulture);
|
|
if (type == typeof(bool)) return (T)(object)bool.Parse(valueString);
|
|
throw new NotSupportedException(type.Name);
|
|
}
|
|
|
|
public object GetParameterDefaultValue(string nodeType, string parameterName)
|
|
{
|
|
switch (nodeType)
|
|
{
|
|
default:
|
|
throw new NodeParameterNotFoundException(parameterName, nodeType);
|
|
}
|
|
}
|
|
|
|
private static T? GetDefaultValue<T>()
|
|
{
|
|
var type = typeof(T);
|
|
if (type == typeof(string)) return (T)(object)string.Empty;
|
|
if (type == typeof(int)) return (T)(object)0;
|
|
if (type == typeof(double)) return (T)(object)0d;
|
|
if (type == typeof(decimal)) return (T)(object)0m;
|
|
if (type == typeof(bool)) return (T)(object)false;
|
|
if (type == typeof(Guid)) return (T)(object)Guid.Empty;
|
|
if (type.IsEnum) return default;
|
|
throw new NotSupportedException(type.Name);
|
|
}
|
|
|
|
public void SetNodeParametersValues<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, object?> parameters) where TNode : INode
|
|
{
|
|
switch (node)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);
|
|
}
|
|
}
|
|
|
|
|
|
private static string GetNodeType(string pipelineId, string nodeId, INode node)
|
|
{
|
|
switch (node)
|
|
{
|
|
default:
|
|
throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);
|
|
}
|
|
}
|
|
|
|
|
|
private record PortConnectionInfo(bool Connected, object? OutputFieldObj, string FromPortName, string FromNodeType, string FromNodeId);
|
|
}
|
|
#nullable restore
|