Skip to content

Commit 04c78c0

Browse files
authored
Merge pull request #584 from TrevorVonSeggern/feature/CaseSensitivity
Case insensitive unit parsing and conversion.
2 parents dfaafa2 + b9d895d commit 04c78c0

File tree

5 files changed

+73
-24
lines changed

5 files changed

+73
-24
lines changed

UnitsNet.Tests/UnitConverterTest.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace UnitsNet.Tests
2626
public class UnitConverterTest
2727
{
2828
[Theory]
29+
[InlineData(0, 0, "length", "meter", "centimeter")]
2930
[InlineData(0, 0, "Length", "Meter", "Centimeter")]
3031
[InlineData(100, 1, "Length", "Meter", "Centimeter")]
3132
[InlineData(1, 1000, "Mass", "Gram", "Kilogram")]
@@ -35,6 +36,18 @@ public void ConvertByName_ConvertsTheValueToGivenUnit(double expectedValue, doub
3536
Assert.Equal(expectedValue, UnitConverter.ConvertByName(inputValue, quantityTypeName, fromUnit, toUnit));
3637
}
3738

39+
[Fact]
40+
public void ConvertByName_QuantityCaseInsensitive()
41+
{
42+
Assert.Equal(0, UnitConverter.ConvertByName(0, "length", "Meter", "Centimeter"));
43+
}
44+
45+
[Fact]
46+
public void ConvertByName_UnitTypeCaseInsensitive()
47+
{
48+
Assert.Equal(0, UnitConverter.ConvertByName(0, "Length", "meter", "Centimeter"));
49+
}
50+
3851
[Theory]
3952
[InlineData(1, "UnknownQuantity", "Meter", "Centimeter")]
4053
public void ConvertByName_ThrowsQuantityNotFoundExceptionOnUnknownQuantity(double inputValue, string quantityTypeName, string fromUnit, string toUnit)

UnitsNet.Tests/UnitParserTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,30 @@ public void Parse_ReturnsUnitMappedByCustomAbbreviation(string customAbbreviatio
4141
Assert.Equal(expected, actual);
4242
}
4343

44+
[Fact]
45+
public void Parse_AbbreviationCaseInsensitive_Lowercase_years()
46+
{
47+
var abbreviation = "years";
48+
var expected = DurationUnit.Year365;
49+
var parser = UnitParser.Default;
50+
51+
var actual = parser.Parse<DurationUnit>(abbreviation);
52+
53+
Assert.Equal(expected, actual);
54+
}
55+
56+
[Fact]
57+
public void Parse_AbbreviationCaseInsensitive_Uppercase_Years()
58+
{
59+
var abbreviation = "Years";
60+
var expected = DurationUnit.Year365;
61+
var parser = UnitParser.Default;
62+
63+
var actual = parser.Parse<DurationUnit>(abbreviation);
64+
65+
Assert.Equal(expected, actual);
66+
}
67+
4468
[Fact]
4569
public void Parse_UnknownAbbreviationThrowsUnitNotFoundException()
4670
{

UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2013 Andreas Gullberg Larsen ([email protected]).
1+
// Copyright (c) 2013 Andreas Gullberg Larsen ([email protected]).
22
// https://github.com/angularsen/UnitsNet
33
//
44
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -63,19 +63,21 @@ internal List<string> GetAbbreviationsForUnit(int unit)
6363

6464
internal List<int> GetUnitsForAbbreviation(string abbreviation)
6565
{
66-
if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var units))
67-
abbreviationToUnitMap[abbreviation] = units = new List<int>();
66+
var lowerCaseAbbreviation = abbreviation.ToLower();
67+
if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var units))
68+
abbreviationToUnitMap[lowerCaseAbbreviation] = units = new List<int>();
6869

6970
return units.Distinct().ToList();
7071
}
7172

7273
internal void Add(int unit, string abbreviation, bool setAsDefault = false)
7374
{
75+
var lowerCaseAbbreviation = abbreviation.ToLower();
7476
if(!unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit))
7577
abbreviationsForUnit = unitToAbbreviationMap[unit] = new List<string>();
7678

77-
if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation))
78-
abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List<int>();
79+
if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var unitsForAbbreviation))
80+
abbreviationToUnitMap[lowerCaseAbbreviation] = unitsForAbbreviation = new List<int>();
7981

8082
abbreviationsForUnit.Remove(abbreviation);
8183
unitsForAbbreviation.Remove(unit);

UnitsNet/InternalHelpers/ReflectionBridgeExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ internal static bool IsEnum(this Type type)
5151
#endif
5252
}
5353

54+
internal static bool IsClass(this Type type)
55+
{
56+
#if !(NET40 || NET35 || NET20 || SILVERLIGHT)
57+
return type.GetTypeInfo().IsClass;
58+
#else
59+
return type.IsClass;
60+
#endif
61+
}
62+
5463
internal static bool IsValueType(this Type type)
5564
{
5665
#if !(NET40 || NET35 || NET20 || SILVERLIGHT)

UnitsNet/UnitConverter.cs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,18 @@ namespace UnitsNet
4040
/// </summary>
4141
public static class UnitConverter
4242
{
43-
private static readonly string QuantityNamespace = typeof(Length).Namespace;
4443
private static readonly string UnitTypeNamespace = typeof(LengthUnit).Namespace;
4544
private static readonly Assembly UnitsNetAssembly = typeof(Length).GetAssembly();
4645

46+
private static readonly Type[] QuantityTypes = UnitsNetAssembly.GetTypes()
47+
.Where(typeof(IQuantity).IsAssignableFrom)
48+
.Where(x => x.IsClass() || x.IsValueType()) // Future-proofing: we are discussing changing quantities from struct to class
49+
.ToArray();
50+
51+
private static readonly Type[] UnitTypes = UnitsNetAssembly.GetTypes()
52+
.Where(x => x.Namespace == UnitTypeNamespace && x.IsEnum() && x.Name.EndsWith("Unit"))
53+
.ToArray();
54+
4755
/// <summary>
4856
/// Convert between any two quantity units by their names, such as converting a "Length" of N "Meter" to "Centimeter".
4957
/// This is particularly useful for creating things like a generated unit conversion UI,
@@ -72,7 +80,7 @@ public static class UnitConverter
7280
/// <returns>Output value as the result of converting to <paramref name="toUnit" />.</returns>
7381
/// <exception cref="QuantityNotFoundException">No quantities were found that match <paramref name="quantityName" />.</exception>
7482
/// <exception cref="UnitNotFoundException">No units match the abbreviation.</exception>
75-
/// <exception cref="AmbiguousUnitParseException">More than one unit matches the abbrevation.</exception>
83+
/// <exception cref="AmbiguousUnitParseException">More than one unit matches the abbreviation.</exception>
7684
public static double ConvertByName(FromValue fromValue, string quantityName, string fromUnit, string toUnit)
7785
{
7886
if(!TryGetQuantityType(quantityName, out var quantityType))
@@ -219,7 +227,7 @@ public static double ConvertByAbbreviation(FromValue fromValue, string quantityN
219227
/// <returns>Output value as the result of converting to <paramref name="toUnitAbbrev" />.</returns>
220228
/// <exception cref="QuantityNotFoundException">No quantity types match the <paramref name="quantityName"/>.</exception>
221229
/// <exception cref="UnitNotFoundException">No unit types match the prefix of <paramref name="quantityName"/> or no units are mapped to the abbreviation.</exception>
222-
/// <exception cref="AmbiguousUnitParseException">More than one unit matches the abbrevation.</exception>
230+
/// <exception cref="AmbiguousUnitParseException">More than one unit matches the abbreviation.</exception>
223231
public static double ConvertByAbbreviation(FromValue fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, string culture)
224232
{
225233
if(!TryGetQuantityType(quantityName, out var quantityType))
@@ -384,37 +392,30 @@ private static bool HasParameterTypes(MethodInfo methodInfo, params Type[] expec
384392
private static bool TryParseUnit(Type unitType, string unitName, out object unitValue)
385393
{
386394
unitValue = null;
387-
388-
if(!Enum.IsDefined(unitType, unitName))
395+
var eNames = Enum.GetNames(unitType);
396+
unitName = eNames.FirstOrDefault(x => x.Equals(unitName, StringComparison.OrdinalIgnoreCase));
397+
if(unitName == null)
389398
return false;
390399

391400
unitValue = Enum.Parse(unitType, unitName);
392-
if(unitValue == null)
393-
return false;
394-
395401
return true;
396402
}
397403

398404
private static bool TryGetUnitType(string quantityName, out Type unitType)
399405
{
400-
string unitTypeName = $"{UnitTypeNamespace}.{quantityName}Unit";
406+
var unitTypeName = quantityName + "Unit"; // ex. LengthUnit
401407

402-
unitType = UnitsNetAssembly.GetType(unitTypeName); // ex: UnitsNet.Units.LengthUnit enum
403-
if(unitType == null)
404-
return false;
408+
unitType = UnitTypes.FirstOrDefault(x =>
409+
x.Name.Equals(unitTypeName, StringComparison.OrdinalIgnoreCase));
405410

406-
return true;
411+
return unitType != null;
407412
}
408413

409414
private static bool TryGetQuantityType(string quantityName, out Type quantityType)
410415
{
411-
string quantityTypeName = $"{QuantityNamespace}.{quantityName}";
416+
quantityType = QuantityTypes.FirstOrDefault(x => x.Name.Equals(quantityName, StringComparison.OrdinalIgnoreCase));
412417

413-
quantityType = UnitsNetAssembly.GetType(quantityTypeName); // ex: UnitsNet.Length struct
414-
if(quantityType == null)
415-
return false;
416-
417-
return true;
418+
return quantityType != null;
418419
}
419420
}
420421
}

0 commit comments

Comments
 (0)