diff --git a/UnitsNet.Tests/UnitConverterTest.cs b/UnitsNet.Tests/UnitConverterTest.cs index f656ea3a14..75ce44a1b6 100644 --- a/UnitsNet.Tests/UnitConverterTest.cs +++ b/UnitsNet.Tests/UnitConverterTest.cs @@ -26,6 +26,7 @@ namespace UnitsNet.Tests public class UnitConverterTest { [Theory] + [InlineData(0, 0, "length", "meter", "centimeter")] [InlineData(0, 0, "Length", "Meter", "Centimeter")] [InlineData(100, 1, "Length", "Meter", "Centimeter")] [InlineData(1, 1000, "Mass", "Gram", "Kilogram")] @@ -35,6 +36,18 @@ public void ConvertByName_ConvertsTheValueToGivenUnit(double expectedValue, doub Assert.Equal(expectedValue, UnitConverter.ConvertByName(inputValue, quantityTypeName, fromUnit, toUnit)); } + [Fact] + public void ConvertByName_QuantityCaseInsensitive() + { + Assert.Equal(0, UnitConverter.ConvertByName(0, "length", "Meter", "Centimeter")); + } + + [Fact] + public void ConvertByName_UnitTypeCaseInsensitive() + { + Assert.Equal(0, UnitConverter.ConvertByName(0, "Length", "meter", "Centimeter")); + } + [Theory] [InlineData(1, "UnknownQuantity", "Meter", "Centimeter")] public void ConvertByName_ThrowsQuantityNotFoundExceptionOnUnknownQuantity(double inputValue, string quantityTypeName, string fromUnit, string toUnit) diff --git a/UnitsNet.Tests/UnitParserTests.cs b/UnitsNet.Tests/UnitParserTests.cs index a6d1dafd8f..da2009e2e0 100644 --- a/UnitsNet.Tests/UnitParserTests.cs +++ b/UnitsNet.Tests/UnitParserTests.cs @@ -41,6 +41,30 @@ public void Parse_ReturnsUnitMappedByCustomAbbreviation(string customAbbreviatio Assert.Equal(expected, actual); } + [Fact] + public void Parse_AbbreviationCaseInsensitive_Lowercase_years() + { + var abbreviation = "years"; + var expected = DurationUnit.Year365; + var parser = UnitParser.Default; + + var actual = parser.Parse(abbreviation); + + Assert.Equal(expected, actual); + } + + [Fact] + public void Parse_AbbreviationCaseInsensitive_Uppercase_Years() + { + var abbreviation = "Years"; + var expected = DurationUnit.Year365; + var parser = UnitParser.Default; + + var actual = parser.Parse(abbreviation); + + Assert.Equal(expected, actual); + } + [Fact] public void Parse_UnknownAbbreviationThrowsUnitNotFoundException() { diff --git a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs index 28ee6faac8..5cceceee9d 100644 --- a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs +++ b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). +// Copyright (c) 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). // https://github.com/angularsen/UnitsNet // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -63,19 +63,21 @@ internal List GetAbbreviationsForUnit(int unit) internal List GetUnitsForAbbreviation(string abbreviation) { - if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var units)) - abbreviationToUnitMap[abbreviation] = units = new List(); + var lowerCaseAbbreviation = abbreviation.ToLower(); + if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var units)) + abbreviationToUnitMap[lowerCaseAbbreviation] = units = new List(); return units.Distinct().ToList(); } internal void Add(int unit, string abbreviation, bool setAsDefault = false) { + var lowerCaseAbbreviation = abbreviation.ToLower(); if(!unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit)) abbreviationsForUnit = unitToAbbreviationMap[unit] = new List(); - if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation)) - abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List(); + if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var unitsForAbbreviation)) + abbreviationToUnitMap[lowerCaseAbbreviation] = unitsForAbbreviation = new List(); abbreviationsForUnit.Remove(abbreviation); unitsForAbbreviation.Remove(unit); diff --git a/UnitsNet/InternalHelpers/ReflectionBridgeExtensions.cs b/UnitsNet/InternalHelpers/ReflectionBridgeExtensions.cs index 993f389f96..e57aa2429e 100644 --- a/UnitsNet/InternalHelpers/ReflectionBridgeExtensions.cs +++ b/UnitsNet/InternalHelpers/ReflectionBridgeExtensions.cs @@ -51,6 +51,15 @@ internal static bool IsEnum(this Type type) #endif } + internal static bool IsClass(this Type type) + { +#if !(NET40 || NET35 || NET20 || SILVERLIGHT) + return type.GetTypeInfo().IsClass; +#else + return type.IsClass; +#endif + } + internal static bool IsValueType(this Type type) { #if !(NET40 || NET35 || NET20 || SILVERLIGHT) diff --git a/UnitsNet/UnitConverter.cs b/UnitsNet/UnitConverter.cs index cf5ac3a15a..6b83b9a630 100644 --- a/UnitsNet/UnitConverter.cs +++ b/UnitsNet/UnitConverter.cs @@ -40,10 +40,18 @@ namespace UnitsNet /// public static class UnitConverter { - private static readonly string QuantityNamespace = typeof(Length).Namespace; private static readonly string UnitTypeNamespace = typeof(LengthUnit).Namespace; private static readonly Assembly UnitsNetAssembly = typeof(Length).GetAssembly(); + private static readonly Type[] QuantityTypes = UnitsNetAssembly.GetTypes() + .Where(typeof(IQuantity).IsAssignableFrom) + .Where(x => x.IsClass() || x.IsValueType()) // Future-proofing: we are discussing changing quantities from struct to class + .ToArray(); + + private static readonly Type[] UnitTypes = UnitsNetAssembly.GetTypes() + .Where(x => x.Namespace == UnitTypeNamespace && x.IsEnum() && x.Name.EndsWith("Unit")) + .ToArray(); + /// /// Convert between any two quantity units by their names, such as converting a "Length" of N "Meter" to "Centimeter". /// This is particularly useful for creating things like a generated unit conversion UI, @@ -72,7 +80,7 @@ public static class UnitConverter /// Output value as the result of converting to . /// No quantities were found that match . /// No units match the abbreviation. - /// More than one unit matches the abbrevation. + /// More than one unit matches the abbreviation. public static double ConvertByName(FromValue fromValue, string quantityName, string fromUnit, string toUnit) { if(!TryGetQuantityType(quantityName, out var quantityType)) @@ -219,7 +227,7 @@ public static double ConvertByAbbreviation(FromValue fromValue, string quantityN /// Output value as the result of converting to . /// No quantity types match the . /// No unit types match the prefix of or no units are mapped to the abbreviation. - /// More than one unit matches the abbrevation. + /// More than one unit matches the abbreviation. public static double ConvertByAbbreviation(FromValue fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, string culture) { if(!TryGetQuantityType(quantityName, out var quantityType)) @@ -384,37 +392,30 @@ private static bool HasParameterTypes(MethodInfo methodInfo, params Type[] expec private static bool TryParseUnit(Type unitType, string unitName, out object unitValue) { unitValue = null; - - if(!Enum.IsDefined(unitType, unitName)) + var eNames = Enum.GetNames(unitType); + unitName = eNames.FirstOrDefault(x => x.Equals(unitName, StringComparison.OrdinalIgnoreCase)); + if(unitName == null) return false; unitValue = Enum.Parse(unitType, unitName); - if(unitValue == null) - return false; - return true; } private static bool TryGetUnitType(string quantityName, out Type unitType) { - string unitTypeName = $"{UnitTypeNamespace}.{quantityName}Unit"; + var unitTypeName = quantityName + "Unit"; // ex. LengthUnit - unitType = UnitsNetAssembly.GetType(unitTypeName); // ex: UnitsNet.Units.LengthUnit enum - if(unitType == null) - return false; + unitType = UnitTypes.FirstOrDefault(x => + x.Name.Equals(unitTypeName, StringComparison.OrdinalIgnoreCase)); - return true; + return unitType != null; } private static bool TryGetQuantityType(string quantityName, out Type quantityType) { - string quantityTypeName = $"{QuantityNamespace}.{quantityName}"; + quantityType = QuantityTypes.FirstOrDefault(x => x.Name.Equals(quantityName, StringComparison.OrdinalIgnoreCase)); - quantityType = UnitsNetAssembly.GetType(quantityTypeName); // ex: UnitsNet.Length struct - if(quantityType == null) - return false; - - return true; + return quantityType != null; } } }