589 lines
28 KiB
C#
589 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
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 NodeValidatorGenerator
|
|
{
|
|
private const string NullString = "null";
|
|
|
|
private static readonly TypeCode[] NumericTypeCodes =
|
|
[
|
|
TypeCode.Byte, TypeCode.SByte,
|
|
TypeCode.Int16, TypeCode.UInt16,
|
|
TypeCode.Int32, TypeCode.UInt32,
|
|
TypeCode.Int64, TypeCode.UInt64,
|
|
TypeCode.Single, TypeCode.Double,
|
|
TypeCode.Decimal
|
|
];
|
|
|
|
|
|
public static string Generate(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("// ReSharper disable RedundantUsingDirective");
|
|
sb.AppendLine("using System.Globalization;");
|
|
sb.AppendLine("using NodePipeline.Abstractions;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Exceptions.Validation;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Interfaces;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Interfaces.Nodes;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Interfaces.Validation;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Models.Validation;");
|
|
sb.AppendLine("using NodePipeline.Abstractions.Validators;");
|
|
sb.AppendLine("using NodePipeline.Configuration.Abstractions.Models.Execute;");
|
|
sb.AppendLine("using NodePipeline.Engine.Abstractions;");
|
|
sb.AppendLine("using NodePipeline.Engine.NodeFields;");
|
|
sb.AppendLine("using NodePipeline.Engine.Utils;");
|
|
sb.AppendLine("using NodePipeline.Engine.Abstractions.Validation;");
|
|
sb.AppendLine();
|
|
sb.AppendLine("// ReSharper disable CheckNamespace");
|
|
sb.AppendLine("// ReSharper disable RedundantNameQualifier");
|
|
sb.AppendLine("// ReSharper disable UnusedMember.Local");
|
|
sb.AppendLine("// ReSharper disable UnusedParameter.Local");
|
|
sb.AppendLine("// ReSharper disable UnusedVariable");
|
|
sb.AppendLine("#nullable enable");
|
|
sb.AppendLine();
|
|
sb.AppendLine("namespace NodePipeline.Engine.Generated;");
|
|
sb.AppendLine();
|
|
sb.AppendLine("public sealed class GeneratedNodeValidator : IPipelineNodeValidator");
|
|
sb.AppendLine("{");
|
|
sb.AppendLine(" private Dictionary<NodePortKey, NodePortInfo>? _allPorts;");
|
|
sb.AppendLine(" public Dictionary<Type, INodeValidator> NodeValidators { get; set; } = [];");
|
|
sb.AppendLine(" public Dictionary<Type, INodeFieldValidator> NodeFieldValidators { get; set; } = [];");
|
|
sb.AppendLine(
|
|
" public IPipelineLocalizationProvider PipelineLocalizationProvider { get; set; } = new PipelineLocalizationProvider();");
|
|
sb.AppendLine();
|
|
|
|
sb.AppendLine(BuildValidateNodeMethod(model));
|
|
|
|
sb.AppendLine(BuildValidateNodeSpecificMethods(model));
|
|
|
|
sb.AppendLine(BuildGetAllPortsMethod());
|
|
sb.AppendLine(BuildGetNodeFieldValidatorMethod());
|
|
sb.AppendLine(BuildValidateNodePortsMethod());
|
|
sb.AppendLine(BuildValidateInputPortMethod());
|
|
sb.AppendLine(BuildGetNodeInputKeyMethod());
|
|
sb.AppendLine(BuildGetNodeValidatorMethod());
|
|
sb.AppendLine(BuildCheckRequiredNodeParameterMethod());
|
|
sb.AppendLine(BuildSuccessNodeValidationResultMethod());
|
|
sb.AppendLine(BuildGetErrorValidationResultMethod());
|
|
sb.AppendLine(BuildSuccessNodeFieldValidationResultMethod());
|
|
sb.AppendLine(BuildUpdateValidationResultMethod());
|
|
|
|
|
|
sb.AppendLine("}");
|
|
sb.AppendLine("#nullable restore");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSpecificValidateNodeMethodName(NodeDescriptor node)
|
|
{
|
|
return $"Validate__{node.TypeNameShortSanitized}__Node";
|
|
}
|
|
|
|
private static string BuildValidateNodeMethod(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" public NodeValidationResult ValidateNode(string pipelineId, INodeFactory nodeFactory, NodeConfig nodeConfig, List<NodeConfig> allNodes, Dictionary<string, object?> parameters, CultureInfo cultureInfo)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var node = nodeFactory.CreateNode(pipelineId, nodeConfig);");
|
|
sb.AppendLine(" nodeFactory.SetNodeParametersValues(pipelineId, node, nodeConfig.Id, parameters);");
|
|
sb.AppendLine(" var nodeConnections = allNodes.SelectMany(q => q.Inputs");
|
|
sb.AppendLine(
|
|
" .Select(x => new KeyValuePair<string, InputSource>(GetNodeInputKey(q.Id, x.Key), x.Value))");
|
|
sb.AppendLine(" ).ToDictionary(q => q.Key, q => q.Value);");
|
|
sb.AppendLine(" var ports = GetAllPorts(pipelineId, nodeFactory, allNodes);");
|
|
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 {GetSpecificValidateNodeMethodName(n)}(pipelineId, nodeFactory, t{i}, nodeConfig.Id, nodeConnections, ports, cultureInfo);");
|
|
i++;
|
|
}
|
|
|
|
sb.AppendLine(" default:");
|
|
sb.AppendLine(" throw new UnknownNodeTypeException(nodeConfig.Type, nodeConfig.Id);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildValidateNodeSpecificMethods(NodeModelBuilder.NodesModel model)
|
|
{
|
|
var sb = new StringBuilder();
|
|
const string tab = " ";
|
|
foreach (var n in model.Nodes)
|
|
{
|
|
var parameterValidatorsCode = CreateParameterValidatorsMethods(n, tab, out var parameterValidatorsCount);
|
|
|
|
var nodeParameters = n.Fields
|
|
.Where(f => f.Direction == FieldDirection.Parameter)
|
|
.ToList();
|
|
|
|
sb.AppendLine(
|
|
$" private NodeValidationResult {GetSpecificValidateNodeMethodName(n)}(string pipelineId, INodeFactory nodeFactory, {n.TypeNameFull} node, string nodeId, IReadOnlyDictionary<string, InputSource> nodePortConnections, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, CultureInfo cultureInfo)");
|
|
sb.AppendLine(" {");
|
|
|
|
if (parameterValidatorsCount > 0)
|
|
sb.AppendLine($"{tab}var validationResult = ValidationResult.Valid;");
|
|
|
|
sb.Append($"{tab}var fieldResults = new Dictionary<string, List<NodeFieldValidationResult>>");
|
|
if (nodeParameters.Count > 0)
|
|
{
|
|
sb.AppendLine();
|
|
sb.AppendLine(CreateFieldResultsContentCode(nodeParameters, " "));
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine("();");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
if (parameterValidatorsCount > 0) sb.AppendLine(parameterValidatorsCode);
|
|
|
|
var nodeName = NodeGeneratorHelper.GetNodeName(n);
|
|
sb.AppendLine(
|
|
$"{tab}var portsValidationResult = ValidateNodePorts(pipelineId, nodeFactory, {nodeName}, nodeId, nodePortConnections, ports, out var portResults, cultureInfo);");
|
|
|
|
sb.Append($"{tab}var isValid = ");
|
|
if (parameterValidatorsCount > 0) sb.Append("validationResult != ValidationResult.HasErrors && ");
|
|
sb.AppendLine("portsValidationResult != ValidationResult.HasErrors;");
|
|
|
|
if (parameterValidatorsCount > 0)
|
|
sb.AppendLine($"{tab}UpdateValidationResult(portsValidationResult, ref validationResult);");
|
|
sb.AppendLine($"{tab}return !isValid");
|
|
|
|
sb.Append(" ? GetErrorValidationResult(");
|
|
sb.Append(parameterValidatorsCount > 0 ? "validationResult" : "ValidationResult.HasErrors");
|
|
sb.AppendLine(", nodeId, fieldResults, portResults, cultureInfo)");
|
|
|
|
var nodeValidator = n.CustomValidators.FirstOrDefault();
|
|
if (nodeValidator == null)
|
|
{
|
|
sb.Append(" : SuccessNodeValidationResult(");
|
|
if (parameterValidatorsCount > 0) sb.Append("validationResult == ValidationResult.HasWarnings || ");
|
|
sb.AppendLine("portsValidationResult == ValidationResult.HasWarnings, fieldResults, portResults);");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine($" : GetNodeValidator(typeof({nodeValidator.ValidatorType})).Validate(");
|
|
sb.AppendLine(" new Dictionary<string, object>");
|
|
sb.AppendLine(" {");
|
|
foreach (var p in nodeParameters)
|
|
sb.AppendLine($" {{ \"{p.FieldName}\", node.{p.PropertyName}.Value }},");
|
|
sb.AppendLine(" }, fieldResults, portResults);");
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string CreateParameterValidatorsMethods(NodeDescriptor n, string tab,
|
|
out int parameterValidatorsCount)
|
|
{
|
|
var sb = new StringBuilder();
|
|
var currentValidatorNumber = 1;
|
|
var nodeParameters = n.Fields.Where(f => f.Direction == FieldDirection.Parameter)
|
|
.ToList();
|
|
|
|
foreach (var p in nodeParameters)
|
|
{
|
|
if (HasParameterValueLengthValidator(p, out var minLength, out var maxLength))
|
|
{
|
|
EnsureParameterValueIsString(p.FieldName, n.Type, p.ValueType);
|
|
var validatorCallText =
|
|
$"new StringLengthValidator(PipelineLocalizationProvider, {minLength}, {maxLength})";
|
|
sb.AppendLine(GetValidatorString(p, validatorCallText, ref currentValidatorNumber, tab));
|
|
}
|
|
|
|
|
|
if (HasParameterNumberBoundValidator(p, out var minValue, out var maxValue))
|
|
{
|
|
EnsureParameterValueIsNumber(p.FieldName, n.Type, p.ValueType);
|
|
var validatorCallText =
|
|
$"new NumberRangeValidator<{p.ValueType}>(PipelineLocalizationProvider, {minValue}, {maxValue})";
|
|
sb.AppendLine(GetValidatorString(p, validatorCallText, ref currentValidatorNumber, tab));
|
|
}
|
|
|
|
if (p.Metadata?.IsRequired == true)
|
|
sb.AppendLine(AddRequiredValidatorCode(p, ref currentValidatorNumber, tab));
|
|
|
|
if (p.Metadata == null) continue;
|
|
|
|
foreach (var validator in p.Metadata.CustomValidators)
|
|
{
|
|
var validatorCallText = validator.CanBeInitialized
|
|
? $"{validator.ValidatorType}()"
|
|
: $"GetNodeFieldValidator<{p.ValueType}>(typeof({validator.ValidatorType}))";
|
|
sb.AppendLine(GetValidatorString(p, validatorCallText, ref currentValidatorNumber, tab));
|
|
}
|
|
}
|
|
|
|
parameterValidatorsCount = currentValidatorNumber - 1;
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string CreateFieldResultsContentCode(List<NodeFieldDescriptor> nodeParameters, string tab)
|
|
{
|
|
var sb = new StringBuilder();
|
|
|
|
sb.AppendLine($"{tab}{{");
|
|
foreach (var p in nodeParameters) sb.AppendLine($" {{ \"{p.FieldName}\", [] }},");
|
|
sb.AppendLine($"{tab}}};");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetValidatorString(NodeFieldDescriptor parameter, string validatorCallText,
|
|
ref int currentValidatorNumber, string tab)
|
|
{
|
|
var sb = new StringBuilder();
|
|
currentValidatorNumber++;
|
|
|
|
sb.AppendLine(
|
|
$"{tab}var r{currentValidatorNumber} = {validatorCallText}.Validate(node.{parameter.PropertyName});");
|
|
sb.AppendLine($"{tab}fieldResults[\"{parameter.FieldName}\"].Add(r{currentValidatorNumber});");
|
|
sb.AppendLine($"{tab}UpdateValidationResult(r{currentValidatorNumber}.Result, ref validationResult);");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static bool HasParameterValueLengthValidator(NodeFieldDescriptor parameter, out string minLength,
|
|
out string maxLength)
|
|
{
|
|
if (parameter.Metadata is null ||
|
|
(parameter.Metadata?.StringMaxLength is null && parameter.Metadata?.StringMinLength is null))
|
|
{
|
|
minLength = NumberToString(null);
|
|
maxLength = NumberToString(null);
|
|
return false;
|
|
}
|
|
|
|
minLength = NumberToString(parameter.Metadata.StringMinLength);
|
|
maxLength = NumberToString(parameter.Metadata.StringMaxLength);
|
|
return true;
|
|
}
|
|
|
|
private static bool HasParameterNumberBoundValidator(NodeFieldDescriptor parameter, out string minValue,
|
|
out string maxValue)
|
|
{
|
|
if (parameter.Metadata is null ||
|
|
(parameter.Metadata?.NumberMinBound is null && parameter.Metadata?.NumberMaxBound is null))
|
|
{
|
|
minValue = NumberToString(null);
|
|
maxValue = NumberToString(null);
|
|
return false;
|
|
}
|
|
|
|
minValue = NumberToString(parameter.Metadata.NumberMinBound);
|
|
maxValue = NumberToString(parameter.Metadata.NumberMaxBound);
|
|
return true;
|
|
}
|
|
|
|
private static string NumberToString(int? value)
|
|
{
|
|
return value?.ToString() ?? NullString;
|
|
}
|
|
|
|
private static string NumberToString(decimal? value)
|
|
{
|
|
return value?.ToString() ?? NullString;
|
|
}
|
|
|
|
private static string AddRequiredValidatorCode(NodeFieldDescriptor parameter, ref int currentValidatorNumber,
|
|
string tab)
|
|
{
|
|
var sb = new StringBuilder();
|
|
currentValidatorNumber++;
|
|
|
|
sb.AppendLine(
|
|
$"{tab}var r{currentValidatorNumber} = CheckRequiredNodeParameter<{parameter.ValueType}>(nodeId, node.{parameter.PropertyName}, \"{parameter.FieldName}\", cultureInfo);");
|
|
sb.AppendLine($"{tab}fieldResults[\"{parameter.FieldName}\"].Add(r{currentValidatorNumber});");
|
|
sb.AppendLine($"{tab}UpdateValidationResult(r{currentValidatorNumber}.Result, ref validationResult);");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
|
|
private static string BuildGetAllPortsMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private IReadOnlyDictionary<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, INodeFactory nodeFactory, List<NodeConfig> allNodes)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var nodeTypes = allNodes.Select(q => q.Type)");
|
|
sb.AppendLine(" .Distinct()");
|
|
sb.AppendLine(" .ToHashSet();");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var nodeList = allNodes");
|
|
sb.AppendLine(" .GroupBy(q => q.Type,");
|
|
sb.AppendLine(" (type, g) => new { Type = type, NodeIds = g.Select(q => q.Id) })");
|
|
sb.AppendLine(" .ToDictionary(q => q.Type, q => q.NodeIds);");
|
|
sb.AppendLine(" _allPorts ??= nodeFactory.GetAllPorts(pipelineId, nodeTypes, nodeList);");
|
|
sb.AppendLine(" return _allPorts.AsReadOnly();");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetNodeFieldValidatorMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private INodeFieldValidator<T> GetNodeFieldValidator<T>(Type validatorType)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" if (!NodeFieldValidators.TryGetValue(validatorType, out var validator)");
|
|
sb.AppendLine(" || validator is not INodeFieldValidator<T> genericValidator)");
|
|
sb.AppendLine(
|
|
" throw new UserDefinedNodeFieldValidatorNotFoundException(validatorType.FullName ?? validatorType.Name);");
|
|
sb.AppendLine(" return genericValidator;");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildValidateNodePortsMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private ValidationResult ValidateNodePorts(string pipelineId, INodeFactory nodeFactory, string nodeType, string nodeId,");
|
|
sb.AppendLine(
|
|
" IReadOnlyDictionary<string, InputSource> nodePortConnections, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports,");
|
|
sb.AppendLine(
|
|
" out Dictionary<string, List<NodeFieldValidationResult>> inputPortValidationResults, CultureInfo cultureInfo)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var result = ValidationResult.Valid;");
|
|
sb.AppendLine(
|
|
" inputPortValidationResults = new Dictionary<string, List<NodeFieldValidationResult>>();");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" var inputPorts = nodeFactory.GetNodeInputPorts(pipelineId, nodeType, nodeId);");
|
|
sb.AppendLine(" foreach (var inputPort in inputPorts)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" var code = NodeFieldCodeGenerator.GenerateFieldCode(nodeId, inputPort.Name,");
|
|
sb.AppendLine(" inputPort.Input ? FieldDirection.Input : FieldDirection.Output);");
|
|
sb.AppendLine(" inputPortValidationResults[code] = [];");
|
|
sb.AppendLine(
|
|
" var hasConnection = nodePortConnections.TryGetValue(GetNodeInputKey(nodeId, inputPort.Name), out var connection);");
|
|
sb.AppendLine(" if (inputPort.Required && !hasConnection)");
|
|
sb.AppendLine(" {");
|
|
|
|
sb.AppendLine(
|
|
" inputPortValidationResults[code].Add(new NodeFieldValidationResult(ValidationResult.HasErrors,");
|
|
sb.AppendLine(
|
|
" PipelineLocalizationProvider.GetLocalizedString(Constants.ValidationResults.ErrorMessages.RequiredInputPortHasNoConnectionError, cultureInfo, inputPort.Name, nodeId)));");
|
|
//TODO: add error code
|
|
sb.AppendLine(" UpdateValidationResult(ValidationResult.HasErrors, ref result);");
|
|
sb.AppendLine(" continue;");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine(" if (!hasConnection || connection == null) continue;");
|
|
sb.AppendLine();
|
|
sb.AppendLine(
|
|
" var portValidationResult = ValidateInputPort(nodeId, inputPort, connection, ports, cultureInfo);");
|
|
sb.AppendLine(" inputPortValidationResults[code].Add(portValidationResult);");
|
|
sb.AppendLine(" UpdateValidationResult(portValidationResult.Result, ref result);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" return result;");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetNodeInputKeyMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private static string GetNodeInputKey(string nodeId, string inputPortName)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" return string.Concat(nodeId, \"_\", inputPortName);");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildValidateInputPortMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private NodeFieldValidationResult ValidateInputPort(string nodeId, NodePortInfo inputPort, InputSource connection,");
|
|
sb.AppendLine(" IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, CultureInfo cultureInfo)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" if (!ports.TryGetValue(new NodePortKey(connection.NodeId, connection.OutputName, false), out var outputPort))");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" return new NodeFieldValidationResult(ValidationResult.HasErrors, PipelineLocalizationProvider");
|
|
sb.AppendLine(
|
|
" .GetLocalizedString(Constants.ValidationResults.ErrorMessages.OutputPortNotFoundError, cultureInfo, connection.OutputName,");
|
|
sb.AppendLine(" connection.NodeId, inputPort.Name, nodeId));");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" if (inputPort.Type != outputPort.Type)");
|
|
sb.AppendLine(" {");
|
|
|
|
sb.AppendLine(
|
|
" return new NodeFieldValidationResult(ValidationResult.HasErrors, PipelineLocalizationProvider");
|
|
sb.AppendLine(
|
|
" .GetLocalizedString(Constants.ValidationResults.ErrorMessages.PortsTypeMismatchError, cultureInfo,");
|
|
sb.AppendLine(
|
|
" inputPort.Name, nodeId, inputPort.Type, connection.OutputName, connection.NodeId, outputPort.Type));");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" if (inputPort.IsNullable == outputPort.IsNullable)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" return SuccessNodeFieldValidationResult();");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" if (!inputPort.IsNullable && outputPort.IsNullable)");
|
|
sb.AppendLine(" {");
|
|
|
|
sb.AppendLine(" return new NodeFieldValidationResult(");
|
|
sb.AppendLine(" ValidationResult.HasWarnings, PipelineLocalizationProvider");
|
|
sb.AppendLine(
|
|
" .GetLocalizedString(Constants.ValidationResults.ErrorMessages.NullableAndNonNullablePortsConnectionWarning, cultureInfo,");
|
|
sb.AppendLine(
|
|
" inputPort.Name, nodeId, inputPort.Type, connection.OutputName, connection.NodeId, outputPort.Type));");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" return SuccessNodeFieldValidationResult();");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetNodeValidatorMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private INodeValidator GetNodeValidator(Type validatorType)");
|
|
sb.AppendLine(" {");
|
|
|
|
sb.AppendLine(" if (!NodeValidators.TryGetValue(validatorType, out var validator))");
|
|
sb.AppendLine(
|
|
" throw new UserDefinedNodeValidatorNotFoundException(validatorType.FullName ?? validatorType.Name);");
|
|
sb.AppendLine(" return validator;");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildCheckRequiredNodeParameterMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private NodeFieldValidationResult CheckRequiredNodeParameter<T>(string nodeId, NodePipeline.Abstractions.Models.NodeField<T> nodeField, string nodeParameterName, CultureInfo cultureInfo)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(" return nodeField.Value is not null");
|
|
sb.AppendLine(" ? new NodeFieldValidationResult(ValidationResult.Valid, null)");
|
|
sb.AppendLine(
|
|
" : new NodeFieldValidationResult(ValidationResult.HasErrors, PipelineLocalizationProvider");
|
|
sb.AppendLine(
|
|
" .GetLocalizedString(Constants.ValidationResults.ErrorMessages.RequiredNodeParameterValidationError, cultureInfo, nodeParameterName, nodeId));");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildSuccessNodeValidationResultMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private static NodeValidationResult SuccessNodeValidationResult(bool hasWarnings, Dictionary<string, List<NodeFieldValidationResult>> fieldResults, Dictionary<string, List<NodeFieldValidationResult>> portResults) =>");
|
|
sb.AppendLine(" new(hasWarnings ? ValidationResult.HasWarnings : ValidationResult.Valid, string.Empty,");
|
|
sb.AppendLine(" fieldResults.AsReadOnly(), portResults.AsReadOnly());");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildGetErrorValidationResultMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private NodeValidationResult GetErrorValidationResult(ValidationResult validationResult, string nodeId, Dictionary<string, List<NodeFieldValidationResult>> fieldResults, Dictionary<string, List<NodeFieldValidationResult>> portResults, CultureInfo cultureInfo) =>");
|
|
sb.AppendLine(
|
|
" new(validationResult, PipelineLocalizationProvider.GetLocalizedString(Constants.ValidationResults.ErrorMessages.NodeValidationError, cultureInfo, nodeId), fieldResults.AsReadOnly(), portResults.AsReadOnly());");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildSuccessNodeFieldValidationResultMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(" private static NodeFieldValidationResult SuccessNodeFieldValidationResult() =>");
|
|
sb.AppendLine(" new NodeFieldValidationResult(ValidationResult.Valid, null);");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildUpdateValidationResultMethod()
|
|
{
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine(
|
|
" private static void UpdateValidationResult(ValidationResult validationResult, ref ValidationResult parametersValidationResult)");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" if (validationResult == parametersValidationResult || validationResult == ValidationResult.Valid) return;");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" parametersValidationResult = validationResult switch");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine(
|
|
" ValidationResult.HasWarnings when parametersValidationResult == ValidationResult.Valid => ValidationResult.HasWarnings,");
|
|
sb.AppendLine(
|
|
" ValidationResult.HasErrors when parametersValidationResult != ValidationResult.HasErrors => ValidationResult.HasErrors,");
|
|
sb.AppendLine(" _ => parametersValidationResult");
|
|
sb.AppendLine(" };");
|
|
sb.AppendLine(" }");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static void EnsureParameterValueIsString(string fieldName, string nodeName, string parameterValueType)
|
|
{
|
|
var shortTypeName = parameterValueType.Split('.').Last();
|
|
|
|
var isString = shortTypeName.Equals("string", StringComparison.OrdinalIgnoreCase)
|
|
|| shortTypeName.Equals("String", StringComparison.OrdinalIgnoreCase);
|
|
if (isString) return;
|
|
ThrowInvalidTypeException(fieldName, nodeName, "string", parameterValueType);
|
|
}
|
|
|
|
private static void EnsureParameterValueIsNumber(string fieldName, string nodeName, string parameterValueType)
|
|
{
|
|
var normalized = NormalizeTypeName(parameterValueType);
|
|
|
|
var type = Type.GetType(normalized, false);
|
|
var typeCode = Type.GetTypeCode(type);
|
|
|
|
if (NumericTypeCodes.Contains(typeCode)) return;
|
|
|
|
var expectedType = $"number ({string.Join(", ", NumericTypeCodes)})";
|
|
ThrowInvalidTypeException(fieldName, nodeName, expectedType, parameterValueType);
|
|
}
|
|
|
|
private static string NormalizeTypeName(string typeName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(typeName)) return typeName;
|
|
typeName = typeName.Replace("global::", "").TrimEnd('?');
|
|
|
|
return typeName switch
|
|
{
|
|
"byte" => "System.Byte",
|
|
"sbyte" => "System.SByte",
|
|
"short" => "System.Int16",
|
|
"ushort" => "System.UInt16",
|
|
"int" => "System.Int32",
|
|
"uint" => "System.UInt32",
|
|
"long" => "System.Int64",
|
|
"ulong" => "System.UInt64",
|
|
"float" => "System.Single",
|
|
"double" => "System.Double",
|
|
"decimal" => "System.Decimal",
|
|
_ => typeName
|
|
};
|
|
}
|
|
|
|
|
|
private static void ThrowInvalidTypeException(string fieldName, string nodeName, string expectedType,
|
|
string realType)
|
|
{
|
|
throw new Exception(
|
|
$"Parameter [{fieldName}] ([{nodeName}] node) value has [{realType}] type but [{expectedType}] is expected.");
|
|
}
|
|
} |