From 7dfbf659f20c8f2ec51745f7ece92fe45cace710 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 11 Jun 2019 17:31:12 +0200 Subject: [PATCH] LogLevel - Added support for TypeConverter (#3469) --- src/NLog/Attributes/LogLevelTypeConverter.cs | 91 +++++++++++++++++++ src/NLog/Internal/ObjectReflectionCache.cs | 4 +- src/NLog/LogLevel.cs | 8 +- src/NLog/NLog.csproj | 1 + .../LayoutRenderers/LogLevelTests.cs | 16 +++- 5 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 src/NLog/Attributes/LogLevelTypeConverter.cs diff --git a/src/NLog/Attributes/LogLevelTypeConverter.cs b/src/NLog/Attributes/LogLevelTypeConverter.cs new file mode 100644 index 0000000000..911d09c2ae --- /dev/null +++ b/src/NLog/Attributes/LogLevelTypeConverter.cs @@ -0,0 +1,91 @@ +// +// Copyright (c) 2004-2019 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Attributes +{ + using System; + using System.ComponentModel; + using System.Globalization; + + /// + /// Support implementation of + /// + public class LogLevelTypeConverter : TypeConverter + { + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string) || IsNumericType(sourceType) || base.CanConvertFrom(context, sourceType); + } + + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value?.GetType() == typeof(string)) + return LogLevel.FromString(value.ToString()); + else if (IsNumericType(value?.GetType())) + return LogLevel.FromOrdinal(Convert.ToInt32(value)); + else + return base.ConvertFrom(context, culture, value); + } + + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(string) || IsNumericType(destinationType) || base.CanConvertTo(context, destinationType); + } + + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (value is LogLevel logLevel) + { + if (destinationType == typeof(string)) + return logLevel.ToString(); + else if (IsNumericType(destinationType)) + return Convert.ChangeType(logLevel.Ordinal, destinationType, culture); + } + return base.ConvertTo(context, culture, value, destinationType); + } + + private static bool IsNumericType(Type sourceType) + { + return sourceType == typeof(int) || + sourceType == typeof(uint) || + sourceType == typeof(long) || + sourceType == typeof(ulong) || + sourceType == typeof(short) || + sourceType == typeof(ushort); + } + } +} \ No newline at end of file diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 525b5e29cc..418f63242f 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -149,7 +149,7 @@ private static bool TryExtractExpandoObject(Type objectType, out ObjectPropertyI private static ObjectPropertyInfos BuildObjectPropertyInfos(object value, Type objectType) { ObjectPropertyInfos propertyInfos; - if (ConvertToString(objectType)) + if (ConvertSimpleToString(objectType)) { propertyInfos = ObjectPropertyInfos.SimpleToString; } @@ -175,7 +175,7 @@ private static ObjectPropertyInfos BuildObjectPropertyInfos(object value, Type o return propertyInfos; } - private static bool ConvertToString(Type objectType) + private static bool ConvertSimpleToString(Type objectType) { if (typeof(IFormattable).IsAssignableFrom(objectType)) return true; diff --git a/src/NLog/LogLevel.cs b/src/NLog/LogLevel.cs index c582f2f7c7..9f115ce5f8 100644 --- a/src/NLog/LogLevel.cs +++ b/src/NLog/LogLevel.cs @@ -35,11 +35,12 @@ namespace NLog { using System; using System.Collections.Generic; - using NLog.Internal; + using System.ComponentModel; /// /// Defines available log levels. /// + [TypeConverter(typeof(Attributes.LogLevelTypeConverter))] public sealed class LogLevel : IComparable, IEquatable, IConvertible { /// @@ -464,7 +465,10 @@ string IConvertible.ToString(IFormatProvider provider) object IConvertible.ToType(Type conversionType, IFormatProvider provider) { - return Convert.ChangeType(_ordinal, conversionType, provider); + if (conversionType == typeof(string)) + return Name; + else + return Convert.ChangeType(_ordinal, conversionType, provider); } ushort IConvertible.ToUInt16(IFormatProvider provider) diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index a66b0c9423..e660eb71f2 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -290,6 +290,7 @@ For all config options and platform support, check https://nlog-project.org/conf + diff --git a/tests/NLog.UnitTests/LayoutRenderers/LogLevelTests.cs b/tests/NLog.UnitTests/LayoutRenderers/LogLevelTests.cs index a34e46c2f2..fbd72e1905 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/LogLevelTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/LogLevelTests.cs @@ -132,16 +132,24 @@ public void LogLevelGetTypeCodeTest() [Theory] [InlineData(typeof(int), 2)] [InlineData(typeof(uint), (uint)2)] + [InlineData(typeof(string), "Info")] public void LogLevelConvertTest(Type type, object expected) { // Arrange - var logLevel = LogLevel.Info; - + IConvertible logLevel = LogLevel.Info; + var logConverter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(LogLevel)); + // Act - var result = Convert.ChangeType(logLevel, type); + var changeTypeResult = Convert.ChangeType(logLevel, type); + var changeToResult = logLevel.ToType(type, System.Globalization.CultureInfo.CurrentCulture); + var convertToResult = logConverter.CanConvertTo(type) ? logConverter.ConvertTo(logLevel, type) : null; + var convertFromResult = logConverter.CanConvertFrom(expected.GetType()) ? logConverter.ConvertFrom(expected) : null; // Assert - Assert.Equal(expected, result); + Assert.Equal(expected, changeTypeResult); + Assert.Equal(expected, changeToResult); + Assert.Equal(expected, convertToResult); + Assert.Equal(logLevel, convertFromResult); } } } \ No newline at end of file