diff --git a/UnitsNet.Tests/UnitParserTests.cs b/UnitsNet.Tests/UnitParserTests.cs index da2009e2e0..3611de2a29 100644 --- a/UnitsNet.Tests/UnitParserTests.cs +++ b/UnitsNet.Tests/UnitParserTests.cs @@ -65,6 +65,13 @@ public void Parse_AbbreviationCaseInsensitive_Uppercase_Years() Assert.Equal(expected, actual); } + [Fact] + public void Parse_GivenAbbreviationsThatAreAmbiguousWhenLowerCase_ReturnsCorrectUnit() + { + Assert.Equal(PressureUnit.Megabar, Pressure.ParseUnit("Mbar")); + Assert.Equal(PressureUnit.Millibar, Pressure.ParseUnit("mbar")); + } + [Fact] public void Parse_UnknownAbbreviationThrowsUnitNotFoundException() { diff --git a/UnitsNet/CustomCode/UnitParser.cs b/UnitsNet/CustomCode/UnitParser.cs index 97d4bffda4..808b18b44f 100644 --- a/UnitsNet/CustomCode/UnitParser.cs +++ b/UnitsNet/CustomCode/UnitParser.cs @@ -90,7 +90,11 @@ object Parse([NotNull] string unitAbbreviation, Type unitType, [CanBeNull] IForm if(!_unitAbbreviationsCache.TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var abbreviations)) throw new UnitNotFoundException($"No abbreviations defined for unit type [{unitType}] for culture [{formatProvider}]."); - var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation); + var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true); + + // Narrow the search if too many hits, for example Megabar "Mbar" and Millibar "mbar" need to be distinguished + if (unitIntValues.Count > 1) + unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: false); switch (unitIntValues.Count) { @@ -191,7 +195,12 @@ bool TryParse(string unitAbbreviation, Type unitType, [CanBeNull] IFormatProvide if(!_unitAbbreviationsCache.TryGetUnitValueAbbreviationLookup(unitType, formatProvider, out var abbreviations)) return false; - var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation); + var unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: true); + + // Narrow the search if too many hits, for example Megabar "Mbar" and Millibar "mbar" need to be distinguished + if (unitIntValues.Count > 1) + unitIntValues = abbreviations.GetUnitsForAbbreviation(unitAbbreviation, ignoreCase: false); + if(unitIntValues.Count != 1) return false; diff --git a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs index 5cceceee9d..c79d17e305 100644 --- a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs +++ b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs @@ -31,14 +31,9 @@ namespace UnitsNet { internal class UnitValueAbbreviationLookup { - private UnitToAbbreviationMap unitToAbbreviationMap = new UnitToAbbreviationMap(); - private AbbreviationToUnitMap abbreviationToUnitMap = new AbbreviationToUnitMap(); - - internal UnitValueAbbreviationLookup() - { - unitToAbbreviationMap = new UnitToAbbreviationMap(); - abbreviationToUnitMap = new AbbreviationToUnitMap(); - } + private readonly UnitToAbbreviationMap unitToAbbreviationMap = new UnitToAbbreviationMap(); + private readonly AbbreviationToUnitMap abbreviationToUnitMap = new AbbreviationToUnitMap(); + private readonly AbbreviationToUnitMap lowerCaseAbbreviationToUnitMap = new AbbreviationToUnitMap(); internal string[] GetAllUnitAbbreviationsForQuantity() { @@ -61,11 +56,14 @@ internal List GetAbbreviationsForUnit(int unit) return abbreviations.Distinct().ToList(); } - internal List GetUnitsForAbbreviation(string abbreviation) + internal List GetUnitsForAbbreviation(string abbreviation, bool ignoreCase) { var lowerCaseAbbreviation = abbreviation.ToLower(); - if(!abbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var units)) - abbreviationToUnitMap[lowerCaseAbbreviation] = units = new List(); + var key = ignoreCase ? lowerCaseAbbreviation : abbreviation; + var map = ignoreCase ? lowerCaseAbbreviationToUnitMap : abbreviationToUnitMap; + + if(!map.TryGetValue(key, out List units)) + map[key] = units = new List(); return units.Distinct().ToList(); } @@ -73,20 +71,28 @@ internal List GetUnitsForAbbreviation(string abbreviation) 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(lowerCaseAbbreviation, out var unitsForAbbreviation)) - abbreviationToUnitMap[lowerCaseAbbreviation] = unitsForAbbreviation = new List(); + if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation)) + abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List(); + + if(!lowerCaseAbbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var unitsForLowerCaseAbbreviation)) + lowerCaseAbbreviationToUnitMap[lowerCaseAbbreviation] = unitsForLowerCaseAbbreviation = new List(); + + unitsForLowerCaseAbbreviation.Remove(unit); + unitsForLowerCaseAbbreviation.Add(unit); - abbreviationsForUnit.Remove(abbreviation); unitsForAbbreviation.Remove(unit); + unitsForAbbreviation.Add(unit); + abbreviationsForUnit.Remove(abbreviation); if (setAsDefault) abbreviationsForUnit.Insert(0, abbreviation); else abbreviationsForUnit.Add(abbreviation); - unitsForAbbreviation.Add(unit); + } } }