Import YamlDotNet.

This commit is contained in:
2022-12-12 22:23:49 +08:00
parent f559cea826
commit 1477e907e6
462 changed files with 27142 additions and 0 deletions

View File

@@ -0,0 +1,282 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization.Utilities;
namespace YamlDotNet.Serialization.NodeDeserializers
{
public sealed class ScalarNodeDeserializer : INodeDeserializer
{
private const string BooleanTruePattern = "^(true|y|yes|on)$";
private const string BooleanFalsePattern = "^(false|n|no|off)$";
bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
{
if (!parser.TryConsume<Scalar>(out var scalar))
{
value = null;
return false;
}
// Strip off the nullable type, if present
var underlyingType = Nullable.GetUnderlyingType(expectedType) ?? expectedType;
if (underlyingType.IsEnum())
{
value = Enum.Parse(underlyingType, scalar.Value, true);
return true;
}
var typeCode = underlyingType.GetTypeCode();
switch (typeCode)
{
case TypeCode.Boolean:
value = DeserializeBooleanHelper(scalar.Value);
break;
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
value = DeserializeIntegerHelper(typeCode, scalar.Value);
break;
case TypeCode.Single:
value = float.Parse(scalar.Value, YamlFormatter.NumberFormat);
break;
case TypeCode.Double:
value = double.Parse(scalar.Value, YamlFormatter.NumberFormat);
break;
case TypeCode.Decimal:
value = decimal.Parse(scalar.Value, YamlFormatter.NumberFormat);
break;
case TypeCode.String:
value = scalar.Value;
break;
case TypeCode.Char:
value = scalar.Value[0];
break;
case TypeCode.DateTime:
// TODO: This is probably incorrect. Use the correct regular expression.
value = DateTime.Parse(scalar.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
break;
default:
if (expectedType == typeof(object))
{
// Default to string
value = scalar.Value;
}
else
{
value = TypeConverter.ChangeType(scalar.Value, expectedType);
}
break;
}
return true;
}
private object DeserializeBooleanHelper(string value)
{
bool result;
if (Regex.IsMatch(value, BooleanTruePattern, RegexOptions.IgnoreCase))
{
result = true;
}
else if (Regex.IsMatch(value, BooleanFalsePattern, RegexOptions.IgnoreCase))
{
result = false;
}
else
{
throw new FormatException($"The value \"{value}\" is not a valid YAML Boolean");
}
return result;
}
private object DeserializeIntegerHelper(TypeCode typeCode, string value)
{
var numberBuilder = new StringBuilder();
var currentIndex = 0;
var isNegative = false;
int numberBase;
ulong result = 0;
if (value[0] == '-')
{
currentIndex++;
isNegative = true;
}
else if (value[0] == '+')
{
currentIndex++;
}
if (value[currentIndex] == '0')
{
// Could be binary, octal, hex, decimal (0)
// If there are no characters remaining, it's a decimal zero
if (currentIndex == value.Length - 1)
{
numberBase = 10;
result = 0;
}
else
{
// Check the next character
currentIndex++;
if (value[currentIndex] == 'b')
{
// Binary
numberBase = 2;
currentIndex++;
}
else if (value[currentIndex] == 'x')
{
// Hex
numberBase = 16;
currentIndex++;
}
else
{
// Octal
numberBase = 8;
}
}
// Copy remaining digits to the number buffer (skip underscores)
while (currentIndex < value.Length)
{
if (value[currentIndex] != '_')
{
numberBuilder.Append(value[currentIndex]);
}
currentIndex++;
}
// Parse the magnitude of the number
switch (numberBase)
{
case 2:
case 8:
// TODO: how to incorporate the numberFormat?
result = Convert.ToUInt64(numberBuilder.ToString(), numberBase);
break;
case 16:
result = ulong.Parse(numberBuilder.ToString(), NumberStyles.HexNumber, YamlFormatter.NumberFormat);
break;
case 10:
// Result is already zero
break;
}
}
else
{
// Could be decimal or base 60
var chunks = value.Substring(currentIndex).Split(':');
result = 0;
for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++)
{
result *= 60;
// TODO: verify that chunks after the first are non-negative and less than 60
result += ulong.Parse(chunks[chunkIndex].Replace("_", ""));
}
}
if (isNegative)
{
return CastInteger(checked(-(long)result), typeCode);
}
else
{
return CastInteger(result, typeCode);
}
}
private static object CastInteger(long number, TypeCode typeCode)
{
checked
{
return typeCode switch
{
TypeCode.Byte => (byte)number,
TypeCode.Int16 => (short)number,
TypeCode.Int32 => (int)number,
TypeCode.Int64 => number,
TypeCode.SByte => (sbyte)number,
TypeCode.UInt16 => (ushort)number,
TypeCode.UInt32 => (uint)number,
TypeCode.UInt64 => (ulong)number,
_ => number,
};
}
}
private static object CastInteger(ulong number, TypeCode typeCode)
{
checked
{
return typeCode switch
{
TypeCode.Byte => (byte)number,
TypeCode.Int16 => (short)number,
TypeCode.Int32 => (int)number,
TypeCode.Int64 => (long)number,
TypeCode.SByte => (sbyte)number,
TypeCode.UInt16 => (ushort)number,
TypeCode.UInt32 => (uint)number,
TypeCode.UInt64 => number,
_ => number,
};
}
}
}
}