Skip to content

Commit ce8751c

Browse files
authored
Merge pull request #591 from angularsen/fix-parsing-ambiguous-lowercase-units
Fix parsing ambiguous abbreviations when lowercase
2 parents bd5fda0 + 5b7df20 commit ce8751c

File tree

3 files changed

+39
-17
lines changed

3 files changed

+39
-17
lines changed

UnitsNet.Tests/UnitParserTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ public void Parse_AbbreviationCaseInsensitive_Uppercase_Years()
6565
Assert.Equal(expected, actual);
6666
}
6767

68+
[Fact]
69+
public void Parse_GivenAbbreviationsThatAreAmbiguousWhenLowerCase_ReturnsCorrectUnit()
70+
{
71+
Assert.Equal(PressureUnit.Megabar, Pressure.ParseUnit("Mbar"));
72+
Assert.Equal(PressureUnit.Millibar, Pressure.ParseUnit("mbar"));
73+
}
74+
6875
[Fact]
6976
public void Parse_UnknownAbbreviationThrowsUnitNotFoundException()
7077
{

UnitsNet/CustomCode/UnitParser.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ object Parse([NotNull] string unitAbbreviation, Type unitType, [CanBeNull] IForm
9090
if(!_unitAbbreviationsCache.TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var abbreviations))
9191
throw new UnitNotFoundException($"No abbreviations defined for unit type [{unitType}] for culture [{formatProvider}].");
9292

93-
var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation);
93+
var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true);
94+
95+
// Narrow the search if too many hits, for example Megabar "Mbar" and Millibar "mbar" need to be distinguished
96+
if (unitIntValues.Count > 1)
97+
unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: false);
9498

9599
switch (unitIntValues.Count)
96100
{
@@ -191,7 +195,12 @@ bool TryParse(string unitAbbreviation, Type unitType, [CanBeNull] IFormatProvide
191195
if(!_unitAbbreviationsCache.TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var abbreviations))
192196
return false;
193197

194-
var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation);
198+
var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true);
199+
200+
// Narrow the search if too many hits, for example Megabar "Mbar" and Millibar "mbar" need to be distinguished
201+
if (unitIntValues.Count > 1)
202+
unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: false);
203+
195204
if(unitIntValues.Count != 1)
196205
return false;
197206

UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,9 @@ namespace UnitsNet
3131
{
3232
internal class UnitValueAbbreviationLookup
3333
{
34-
private UnitToAbbreviationMap unitToAbbreviationMap = new UnitToAbbreviationMap();
35-
private AbbreviationToUnitMap abbreviationToUnitMap = new AbbreviationToUnitMap();
36-
37-
internal UnitValueAbbreviationLookup()
38-
{
39-
unitToAbbreviationMap = new UnitToAbbreviationMap();
40-
abbreviationToUnitMap = new AbbreviationToUnitMap();
41-
}
34+
private readonly UnitToAbbreviationMap unitToAbbreviationMap = new UnitToAbbreviationMap();
35+
private readonly AbbreviationToUnitMap abbreviationToUnitMap = new AbbreviationToUnitMap();
36+
private readonly AbbreviationToUnitMap lowerCaseAbbreviationToUnitMap = new AbbreviationToUnitMap();
4237

4338
internal string[] GetAllUnitAbbreviationsForQuantity()
4439
{
@@ -61,32 +56,43 @@ internal List<string> GetAbbreviationsForUnit(int unit)
6156
return abbreviations.Distinct().ToList();
6257
}
6358

64-
internal List<int> GetUnitsForAbbreviation(string abbreviation)
59+
internal List<int> GetUnitsForAbbreviation(string abbreviation, bool ignoreCase)
6560
{
6661
var lowerCaseAbbreviation = abbreviation.ToLower();
67-
if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var units))
68-
abbreviationToUnitMap[lowerCaseAbbreviation] = units = new List<int>();
62+
var key = ignoreCase ? lowerCaseAbbreviation : abbreviation;
63+
var map = ignoreCase ? lowerCaseAbbreviationToUnitMap : abbreviationToUnitMap;
64+
65+
if(!map.TryGetValue(key, out List<int> units))
66+
map[key] = units = new List<int>();
6967

7068
return units.Distinct().ToList();
7169
}
7270

7371
internal void Add(int unit, string abbreviation, bool setAsDefault = false)
7472
{
7573
var lowerCaseAbbreviation = abbreviation.ToLower();
74+
7675
if(!unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit))
7776
abbreviationsForUnit = unitToAbbreviationMap[unit] = new List<string>();
7877

79-
if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var unitsForAbbreviation))
80-
abbreviationToUnitMap[lowerCaseAbbreviation] = unitsForAbbreviation = new List<int>();
78+
if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation))
79+
abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List<int>();
80+
81+
if(!lowerCaseAbbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var unitsForLowerCaseAbbreviation))
82+
lowerCaseAbbreviationToUnitMap[lowerCaseAbbreviation] = unitsForLowerCaseAbbreviation = new List<int>();
83+
84+
unitsForLowerCaseAbbreviation.Remove(unit);
85+
unitsForLowerCaseAbbreviation.Add(unit);
8186

82-
abbreviationsForUnit.Remove(abbreviation);
8387
unitsForAbbreviation.Remove(unit);
88+
unitsForAbbreviation.Add(unit);
8489

90+
abbreviationsForUnit.Remove(abbreviation);
8591
if (setAsDefault)
8692
abbreviationsForUnit.Insert(0, abbreviation);
8793
else
8894
abbreviationsForUnit.Add(abbreviation);
89-
unitsForAbbreviation.Add(unit);
95+
9096
}
9197
}
9298
}

0 commit comments

Comments
 (0)