From 20e808bd937f19fac168d337e4064b3090028744 Mon Sep 17 00:00:00 2001 From: Greg Dennis Date: Tue, 18 Dec 2018 11:47:44 +1300 Subject: [PATCH 1/6] Added MapUnitToDefaultAbbreviation --- UnitsNet/CustomCode/UnitAbbreviationsCache.cs | 84 ++++++++++++++++++- .../CustomCode/UnitValueAbbreviationLookup.cs | 7 +- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/UnitsNet/CustomCode/UnitAbbreviationsCache.cs b/UnitsNet/CustomCode/UnitAbbreviationsCache.cs index 890fa74658..6701d41b76 100644 --- a/UnitsNet/CustomCode/UnitAbbreviationsCache.cs +++ b/UnitsNet/CustomCode/UnitAbbreviationsCache.cs @@ -90,6 +90,26 @@ void MapUnitToAbbreviation(TUnitType unit, params string[] abbreviati MapUnitToAbbreviation(typeof(TUnitType), Convert.ToInt32(unit), GlobalConfiguration.DefaultCulture, abbreviations); } + /// + /// Adds a unit abbreviation for the given unit enum value and sets it as the default. + /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums + /// in order to or on them later. + /// + /// The unit enum value. + /// Unit abbreviations to add as default. + /// The type of unit enum. + [PublicAPI] + // Windows Runtime Component does not allow public methods/ctors with same number of parameters: https://msdn.microsoft.com/en-us/library/br230301.aspx#Overloaded methods +#if WINDOWS_UWP + internal +#else + public +#endif + void MapUnitToDefaultAbbreviation(TUnitType unit, string abbreviation) where TUnitType : Enum + { + MapUnitToDefaultAbbreviation(typeof(TUnitType), Convert.ToInt32(unit), GlobalConfiguration.DefaultCulture, abbreviation); + } + /// /// Adds one or more unit abbreviation for the given unit enum value. /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums @@ -117,6 +137,33 @@ void MapUnitToAbbreviation(TUnitType unit, IFormatProvider formatProv MapUnitToAbbreviation(unitType, unitValue, formatProvider, abbreviations); } + /// + /// Adds a unit abbreviation for the given unit enum value and sets it as the default. + /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums + /// in order to or on them later. + /// + /// The unit enum value. + /// The format provider to use for lookup. Defaults to if null. + /// Unit abbreviation to add as default. + /// The type of unit enum. + [PublicAPI] + // Windows Runtime Component does not allow public methods/ctors with same number of parameters: https://msdn.microsoft.com/en-us/library/br230301.aspx#Overloaded methods +#if WINDOWS_UWP + internal +#else + public +#endif + void MapUnitToDefaultAbbreviation(TUnitType unit, IFormatProvider formatProvider, string abbreviation) where TUnitType : Enum + { + // Assuming TUnitType is an enum, this conversion is safe. Seems not possible to enforce this today. + // Src: http://stackoverflow.com/questions/908543/how-to-convert-from-system-enum-to-base-integer + // http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum + var unitValue = Convert.ToInt32(unit); + var unitType = typeof(TUnitType); + + MapUnitToDefaultAbbreviation(unitType, unitValue, formatProvider, abbreviation); + } + /// /// Adds one or more unit abbreviation for the given unit enum value. /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums @@ -134,7 +181,7 @@ void MapUnitToAbbreviation(TUnitType unit, IFormatProvider formatProv public #endif void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider formatProvider, [NotNull] params string[] abbreviations) - { + { if (!unitType.IsEnum()) throw new ArgumentException("Must be an enum type.", nameof(unitType)); @@ -155,6 +202,41 @@ void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider formatP } } + /// + /// Adds a unit abbreviation for the given unit enum value and sets it as the default. + /// This is used to dynamically add abbreviations for existing unit enums such as or to extend with third-party unit enums + /// in order to or on them later. + /// + /// The unit enum type. + /// The unit enum value. + /// The format provider to use for lookup. Defaults to if null. + /// Unit abbreviation to add as default. + [PublicAPI] + // Windows Runtime Component does not allow public methods/ctors with same number of parameters: https://msdn.microsoft.com/en-us/library/br230301.aspx#Overloaded methods +#if WINDOWS_UWP + internal +#else + public +#endif + void MapUnitToDefaultAbbreviation(Type unitType, int unitValue, IFormatProvider formatProvider, [NotNull] string abbreviation) + { + if (!unitType.IsEnum()) + throw new ArgumentException("Must be an enum type.", nameof(unitType)); + + if (abbreviation == null) + throw new ArgumentNullException(nameof(abbreviation)); + + formatProvider = formatProvider ?? GlobalConfiguration.DefaultCulture; + + if(!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider)) + quantitiesForProvider = _lookupsForCulture[formatProvider] = new UnitTypeToLookup(); + + if(!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations)) + unitToAbbreviations = quantitiesForProvider[unitType] = new UnitValueAbbreviationLookup(); + + unitToAbbreviations.Add(unitValue, abbreviation, true); + } + /// /// Gets the default abbreviation for a given unit. If a unit has more than one abbreviation defined, then it returns the first one. /// Example: GetDefaultAbbreviation<LengthUnit>(LengthUnit.Kilometer) => "km" diff --git a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs index 18b8e8c171..0c74bd70cd 100644 --- a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs +++ b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs @@ -69,7 +69,7 @@ internal List GetUnitsForAbbreviation(string abbreviation) return units.Distinct().ToList(); } - internal void Add(int unit, string abbreviation) + internal void Add(int unit, string abbreviation, bool useAsDefault = false) { if(!unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit)) abbreviationsForUnit = unitToAbbreviationMap[unit] = new List(); @@ -77,7 +77,10 @@ internal void Add(int unit, string abbreviation) if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation)) abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List(); - abbreviationsForUnit.Add(abbreviation); + if (useAsDefault) + abbreviationsForUnit.Insert(0, abbreviation); + else + abbreviationsForUnit.Add(abbreviation); unitsForAbbreviation.Add(unit); } } From 5c10b12562d132b741f67eb1bab8ef6b45923f1a Mon Sep 17 00:00:00 2001 From: Greg Dennis Date: Tue, 18 Dec 2018 12:00:08 +1300 Subject: [PATCH 2/6] Allow setting existing abbreviations as default --- UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs index 0c74bd70cd..c383874403 100644 --- a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs +++ b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs @@ -77,6 +77,9 @@ internal void Add(int unit, string abbreviation, bool useAsDefault = false) if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation)) abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List(); + abbreviationsForUnit.Remove(abbreviation); + unitsForAbbreviation.Remove(unit); + if (useAsDefault) abbreviationsForUnit.Insert(0, abbreviation); else From 0184162497e3c7d2db8117ab93981f381b3c0b54 Mon Sep 17 00:00:00 2001 From: Greg Dennis Date: Wed, 19 Dec 2018 09:55:06 +1300 Subject: [PATCH 3/6] useAsDefault -> setAsDefault --- UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs index c383874403..28ee6faac8 100644 --- a/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs +++ b/UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs @@ -69,7 +69,7 @@ internal List GetUnitsForAbbreviation(string abbreviation) return units.Distinct().ToList(); } - internal void Add(int unit, string abbreviation, bool useAsDefault = false) + internal void Add(int unit, string abbreviation, bool setAsDefault = false) { if(!unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit)) abbreviationsForUnit = unitToAbbreviationMap[unit] = new List(); @@ -80,7 +80,7 @@ internal void Add(int unit, string abbreviation, bool useAsDefault = false) abbreviationsForUnit.Remove(abbreviation); unitsForAbbreviation.Remove(unit); - if (useAsDefault) + if (setAsDefault) abbreviationsForUnit.Insert(0, abbreviation); else abbreviationsForUnit.Add(abbreviation); From 67535753f5652e747703cbaee1f5f4d74ec210ec Mon Sep 17 00:00:00 2001 From: Greg Dennis Date: Wed, 19 Dec 2018 14:30:06 +1300 Subject: [PATCH 4/6] Extracted common method for adding abbreviations --- UnitsNet/CustomCode/UnitAbbreviationsCache.cs | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/UnitsNet/CustomCode/UnitAbbreviationsCache.cs b/UnitsNet/CustomCode/UnitAbbreviationsCache.cs index 6701d41b76..25c12c7b55 100644 --- a/UnitsNet/CustomCode/UnitAbbreviationsCache.cs +++ b/UnitsNet/CustomCode/UnitAbbreviationsCache.cs @@ -182,24 +182,7 @@ void MapUnitToDefaultAbbreviation(TUnitType unit, IFormatProvider for #endif void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider formatProvider, [NotNull] params string[] abbreviations) { - if (!unitType.IsEnum()) - throw new ArgumentException("Must be an enum type.", nameof(unitType)); - - if (abbreviations == null) - throw new ArgumentNullException(nameof(abbreviations)); - - formatProvider = formatProvider ?? GlobalConfiguration.DefaultCulture; - - if(!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider)) - quantitiesForProvider = _lookupsForCulture[formatProvider] = new UnitTypeToLookup(); - - if(!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations)) - unitToAbbreviations = quantitiesForProvider[unitType] = new UnitValueAbbreviationLookup(); - - foreach(var abbr in abbreviations) - { - unitToAbbreviations.Add(unitValue, abbr); - } + PerformAbbreviationMapping(unitType, unitValue, formatProvider, false, abbreviations); } /// @@ -219,22 +202,30 @@ void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider formatP public #endif void MapUnitToDefaultAbbreviation(Type unitType, int unitValue, IFormatProvider formatProvider, [NotNull] string abbreviation) + { + PerformAbbreviationMapping(unitType, unitValue, formatProvider, true, abbreviation); + } + + private void PerformAbbreviationMapping(Type unitType, int unitValue, IFormatProvider formatProvider, bool setAsDefault, [NotNull] params string[] abbreviations) { if (!unitType.IsEnum()) throw new ArgumentException("Must be an enum type.", nameof(unitType)); - if (abbreviation == null) - throw new ArgumentNullException(nameof(abbreviation)); + if (abbreviations == null) + throw new ArgumentNullException(nameof(abbreviations)); formatProvider = formatProvider ?? GlobalConfiguration.DefaultCulture; - if(!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider)) + if (!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider)) quantitiesForProvider = _lookupsForCulture[formatProvider] = new UnitTypeToLookup(); - if(!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations)) + if (!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations)) unitToAbbreviations = quantitiesForProvider[unitType] = new UnitValueAbbreviationLookup(); - unitToAbbreviations.Add(unitValue, abbreviation, true); + foreach (var abbr in abbreviations) + { + unitToAbbreviations.Add(unitValue, abbr, setAsDefault); + } } /// From 7e9b224c2bc8cb54dff8ee24a86e5ffeb63b192f Mon Sep 17 00:00:00 2001 From: Greg Dennis Date: Thu, 20 Dec 2018 11:48:16 +1300 Subject: [PATCH 5/6] Added unit test for default abbreviation --- UnitsNet.Tests/UnitAbbreviationsCacheTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs b/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs index cfdf98338f..dfadc01e1a 100644 --- a/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs +++ b/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs @@ -308,6 +308,15 @@ public void MapUnitToAbbreviation_AddCustomUnit_DoesNotOverrideDefaultAbbreviati Assert.Equal("m²", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter)); } + [Fact] + public void MapUnitToDefaultAbbreviation_AddCustomAbbreviation_UsesNewAbbreviationWithToString() + { + var cache = new UnitAbbreviationsCache(); + cache.MapUnitToDefaultAbbreviation(AreaUnit.SquareMeter, AmericanCulture, "m^2"); + + Assert.Equal("m^2", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter)); + } + /// /// Convenience method to the proper culture parameter type. /// From 43c6f5be6dde236cdddaaf2fe7363e063f55b815 Mon Sep 17 00:00:00 2001 From: Greg Dennis Date: Fri, 21 Dec 2018 09:28:03 +1300 Subject: [PATCH 6/6] Additional unit test to test ToString() --- UnitsNet.Tests/UnitAbbreviationsCacheTests.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs b/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs index dfadc01e1a..6951c16c36 100644 --- a/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs +++ b/UnitsNet.Tests/UnitAbbreviationsCacheTests.cs @@ -309,12 +309,22 @@ public void MapUnitToAbbreviation_AddCustomUnit_DoesNotOverrideDefaultAbbreviati } [Fact] - public void MapUnitToDefaultAbbreviation_AddCustomAbbreviation_UsesNewAbbreviationWithToString() + public void MapUnitToDefaultAbbreviation_GivenUnitAndCulture_SetsDefaultAbbreviationForUnitAndCulture() { var cache = new UnitAbbreviationsCache(); cache.MapUnitToDefaultAbbreviation(AreaUnit.SquareMeter, AmericanCulture, "m^2"); - Assert.Equal("m^2", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter)); + Assert.Equal("m^2", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter, AmericanCulture)); + } + + [Fact] + public void MapUnitToDefaultAbbreviation_GivenCustomAbbreviation_SetsAbbreviationUsedByQuantityToString() + { + // Use a distinct culture here so that we don't mess up other tests that may rely on the default cache. + var newZealandCulture = GetCulture("en-NZ"); + UnitAbbreviationsCache.Default.MapUnitToDefaultAbbreviation(AreaUnit.SquareMeter, newZealandCulture, "m^2"); + + Assert.Equal("1 m^2", Area.FromSquareMeters(1).ToString(newZealandCulture)); } ///