You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
8.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Resources;
using YamlDotNet.RepresentationModel;
//Yaml流 扩展功能
namespace OptionStudy.UnitApp
{
/// <summary>
/// Yaml流 配置源
/// </summary>
public class YamlStreamConfigurationSource : StreamConfigurationSource
{
/// <summary>
/// Builds the <see cref="YamlStreamConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>An <see cref="YamlStreamConfigurationProvider"/></returns>
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new YamlStreamConfigurationProvider(this);
}
}
/// <summary>
/// Yaml流配置 提供者
/// </summary>
public class YamlStreamConfigurationProvider : StreamConfigurationProvider
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="source">The <see cref="YamlStreamConfigurationSource"/>.
/// </param>
public YamlStreamConfigurationProvider(YamlStreamConfigurationSource source) : base(source) { }
/// <summary>
/// Loads json configuration key/values from a stream into a provider.
/// </summary>
/// <param name="stream">The json <see cref="Stream"/> to load configuration data from.</param>
public override void Load(Stream stream)
{
var parser = new YamlFileParser();
Data = parser?.Parse(stream)??new Dictionary<string, string?>();
}
}
/// <summary>
/// Yaml流 扩展方法类
/// </summary>
public static class YamlConfigurationExtensions
{
public static IConfigurationBuilder AddYamlStreamFile(this IConfigurationBuilder builder, Stream stream)
{
Stream stream2 = stream;
return builder.Add(delegate (YamlStreamConfigurationSource s)
{
s.Stream = stream2;
});
}
}
/// <summary>
/// 解析类:复制原库,因为原库类为 internal
/// </summary>
public class YamlFileParser
{
private readonly IDictionary<string, string?> _data = new SortedDictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _context = new Stack<string>();
private string? _currentPath;
public IDictionary<string, string?> Parse(Stream input)
{
_data.Clear();
_context.Clear();
// https://dotnetfiddle.net/rrR2Bb
var yaml = new YamlStream();
yaml.Load(new StreamReader(input, detectEncodingFromByteOrderMarks: true));
if (yaml.Documents.Any())
{
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
// The document node is a mapping node
VisitYamlMappingNode(mapping);
}
return _data;
}
private void VisitYamlNodePair(KeyValuePair<YamlNode, YamlNode> yamlNodePair)
{
var context = ((YamlScalarNode)yamlNodePair.Key).Value??string.Empty;
VisitYamlNode(context, yamlNodePair.Value);
}
private void VisitYamlNode(string context, YamlNode node)
{
if (node is YamlScalarNode scalarNode)
{
VisitYamlScalarNode(context, scalarNode);
}
if (node is YamlMappingNode mappingNode)
{
VisitYamlMappingNode(context, mappingNode);
}
if (node is YamlSequenceNode sequenceNode)
{
VisitYamlSequenceNode(context, sequenceNode);
}
}
private void VisitYamlScalarNode(string context, YamlScalarNode yamlValue)
{
//a node with a single 1-1 mapping
EnterContext(context);
var currentKey = _currentPath??string.Empty;
if (_data.ContainsKey(currentKey))
{
throw new FormatException(Resources.FormatError_KeyIsDuplicated(currentKey));
}
_data[currentKey] = IsNullValue(yamlValue) ? null : yamlValue.Value;
ExitContext();
}
private void VisitYamlMappingNode(YamlMappingNode node)
{
foreach (var yamlNodePair in node.Children)
{
VisitYamlNodePair(yamlNodePair);
}
}
private void VisitYamlMappingNode(string context, YamlMappingNode yamlValue)
{
//a node with an associated sub-document
EnterContext(context);
VisitYamlMappingNode(yamlValue);
ExitContext();
}
private void VisitYamlSequenceNode(string context, YamlSequenceNode yamlValue)
{
//a node with an associated list
EnterContext(context);
VisitYamlSequenceNode(yamlValue);
ExitContext();
}
private void VisitYamlSequenceNode(YamlSequenceNode node)
{
for (int i = 0; i < node.Children.Count; i++)
{
VisitYamlNode(i.ToString(), node.Children[i]);
}
}
private void EnterContext(string context)
{
_context.Push(context);
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
private void ExitContext()
{
_context.Pop();
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
private bool IsNullValue(YamlScalarNode yamlValue)
{
return yamlValue.Style == YamlDotNet.Core.ScalarStyle.Plain
&& (
yamlValue.Value == "~"
|| yamlValue.Value == "null"
|| yamlValue.Value == "Null"
|| yamlValue.Value == "NULL"
);
}
}
/// <summary>
/// 解析依赖类:复制原库,因为原库类为 internal
/// </summary>
public static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("NetEscapades.Configuration.Yaml.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The configuration file '{0}' was not found and is not optional.
/// </summary>
internal static string Error_FileNotFound
{
get { return GetString("Error_FileNotFound"); }
}
/// <summary>
/// The configuration file '{0}' was not found and is not optional.
/// </summary>
internal static string FormatError_FileNotFound(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Error_FileNotFound"), p0);
}
/// <summary>
/// File path must be a non-empty string.
/// </summary>
internal static string Error_InvalidFilePath
{
get { return GetString("Error_InvalidFilePath"); }
}
/// <summary>
/// File path must be a non-empty string.
/// </summary>
internal static string FormatError_InvalidFilePath()
{
return GetString("Error_InvalidFilePath");
}
/// <summary>
/// Could not parse the Yaml file. Error on line number '{0}': '{1}'.
/// </summary>
internal static string Error_YamlParseError
{
get { return GetString("Error_YamlParseError"); }
}
/// <summary>
/// Could not parse the YAML file: {0}.
/// </summary>
internal static string FormatError_YamlParseError(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Error_YamlParseError"), p0);
}
/// <summary>
/// A duplicate key '{0}' was found.
/// </summary>
internal static string Error_KeyIsDuplicated
{
get { return GetString("Error_KeyIsDuplicated"); }
}
/// <summary>
/// A duplicate key '{0}' was found.
/// </summary>
internal static string FormatError_KeyIsDuplicated(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Error_KeyIsDuplicated"), p0);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}