Skip to content

Commit 91a7589

Browse files
gregsdennisangularsen
authored andcommitted
Added MapUnitToDefaultAbbreviation (#578)
* Allow setting existing abbreviations as default * Added unit test for default abbreviation * Additional unit test to test ToString()
1 parent 9798dcd commit 91a7589

File tree

3 files changed

+105
-7
lines changed

3 files changed

+105
-7
lines changed

UnitsNet.Tests/UnitAbbreviationsCacheTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,25 @@ public void MapUnitToAbbreviation_AddCustomUnit_DoesNotOverrideDefaultAbbreviati
308308
Assert.Equal("m²", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter));
309309
}
310310

311+
[Fact]
312+
public void MapUnitToDefaultAbbreviation_GivenUnitAndCulture_SetsDefaultAbbreviationForUnitAndCulture()
313+
{
314+
var cache = new UnitAbbreviationsCache();
315+
cache.MapUnitToDefaultAbbreviation(AreaUnit.SquareMeter, AmericanCulture, "m^2");
316+
317+
Assert.Equal("m^2", cache.GetDefaultAbbreviation(AreaUnit.SquareMeter, AmericanCulture));
318+
}
319+
320+
[Fact]
321+
public void MapUnitToDefaultAbbreviation_GivenCustomAbbreviation_SetsAbbreviationUsedByQuantityToString()
322+
{
323+
// Use a distinct culture here so that we don't mess up other tests that may rely on the default cache.
324+
var newZealandCulture = GetCulture("en-NZ");
325+
UnitAbbreviationsCache.Default.MapUnitToDefaultAbbreviation(AreaUnit.SquareMeter, newZealandCulture, "m^2");
326+
327+
Assert.Equal("1 m^2", Area.FromSquareMeters(1).ToString(newZealandCulture));
328+
}
329+
311330
/// <summary>
312331
/// Convenience method to the proper culture parameter type.
313332
/// </summary>

UnitsNet/CustomCode/UnitAbbreviationsCache.cs

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,26 @@ void MapUnitToAbbreviation<TUnitType>(TUnitType unit, params string[] abbreviati
9090
MapUnitToAbbreviation(typeof(TUnitType), Convert.ToInt32(unit), GlobalConfiguration.DefaultCulture, abbreviations);
9191
}
9292

93+
/// <summary>
94+
/// Adds a unit abbreviation for the given unit enum value and sets it as the default.
95+
/// This is used to dynamically add abbreviations for existing unit enums such as <see cref="UnitsNet.Units.LengthUnit"/> or to extend with third-party unit enums
96+
/// in order to <see cref="UnitParser.Parse{TUnitType}"/> or <see cref="GetDefaultAbbreviation{TUnitType}"/> on them later.
97+
/// </summary>
98+
/// <param name="unit">The unit enum value.</param>
99+
/// <param name="abbreviation">Unit abbreviations to add as default.</param>
100+
/// <typeparam name="TUnitType">The type of unit enum.</typeparam>
101+
[PublicAPI]
102+
// 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
103+
#if WINDOWS_UWP
104+
internal
105+
#else
106+
public
107+
#endif
108+
void MapUnitToDefaultAbbreviation<TUnitType>(TUnitType unit, string abbreviation) where TUnitType : Enum
109+
{
110+
MapUnitToDefaultAbbreviation(typeof(TUnitType), Convert.ToInt32(unit), GlobalConfiguration.DefaultCulture, abbreviation);
111+
}
112+
93113
/// <summary>
94114
/// Adds one or more unit abbreviation for the given unit enum value.
95115
/// This is used to dynamically add abbreviations for existing unit enums such as <see cref="LengthUnit"/> or to extend with third-party unit enums
@@ -117,6 +137,33 @@ void MapUnitToAbbreviation<TUnitType>(TUnitType unit, IFormatProvider formatProv
117137
MapUnitToAbbreviation(unitType, unitValue, formatProvider, abbreviations);
118138
}
119139

140+
/// <summary>
141+
/// Adds a unit abbreviation for the given unit enum value and sets it as the default.
142+
/// This is used to dynamically add abbreviations for existing unit enums such as <see cref="LengthUnit"/> or to extend with third-party unit enums
143+
/// in order to <see cref="UnitParser.Parse{TUnitType}"/> or <see cref="GetDefaultAbbreviation{TUnitType}"/> on them later.
144+
/// </summary>
145+
/// <param name="unit">The unit enum value.</param>
146+
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="GlobalConfiguration.DefaultCulture" /> if null.</param>
147+
/// <param name="abbreviation">Unit abbreviation to add as default.</param>
148+
/// <typeparam name="TUnitType">The type of unit enum.</typeparam>
149+
[PublicAPI]
150+
// 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
151+
#if WINDOWS_UWP
152+
internal
153+
#else
154+
public
155+
#endif
156+
void MapUnitToDefaultAbbreviation<TUnitType>(TUnitType unit, IFormatProvider formatProvider, string abbreviation) where TUnitType : Enum
157+
{
158+
// Assuming TUnitType is an enum, this conversion is safe. Seems not possible to enforce this today.
159+
// Src: http://stackoverflow.com/questions/908543/how-to-convert-from-system-enum-to-base-integer
160+
// http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum
161+
var unitValue = Convert.ToInt32(unit);
162+
var unitType = typeof(TUnitType);
163+
164+
MapUnitToDefaultAbbreviation(unitType, unitValue, formatProvider, abbreviation);
165+
}
166+
120167
/// <summary>
121168
/// Adds one or more unit abbreviation for the given unit enum value.
122169
/// This is used to dynamically add abbreviations for existing unit enums such as <see cref="LengthUnit"/> or to extend with third-party unit enums
@@ -134,7 +181,33 @@ void MapUnitToAbbreviation<TUnitType>(TUnitType unit, IFormatProvider formatProv
134181
public
135182
#endif
136183
void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider formatProvider, [NotNull] params string[] abbreviations)
137-
{
184+
{
185+
PerformAbbreviationMapping(unitType, unitValue, formatProvider, false, abbreviations);
186+
}
187+
188+
/// <summary>
189+
/// Adds a unit abbreviation for the given unit enum value and sets it as the default.
190+
/// This is used to dynamically add abbreviations for existing unit enums such as <see cref="LengthUnit"/> or to extend with third-party unit enums
191+
/// in order to <see cref="UnitParser.Parse{TUnitType}"/> or <see cref="GetDefaultAbbreviation{TUnitType}"/> on them later.
192+
/// </summary>
193+
/// <param name="unitType">The unit enum type.</param>
194+
/// <param name="unitValue">The unit enum value.</param>
195+
/// <param name="formatProvider">The format provider to use for lookup. Defaults to <see cref="GlobalConfiguration.DefaultCulture" /> if null.</param>
196+
/// <param name="abbreviation">Unit abbreviation to add as default.</param>
197+
[PublicAPI]
198+
// 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
199+
#if WINDOWS_UWP
200+
internal
201+
#else
202+
public
203+
#endif
204+
void MapUnitToDefaultAbbreviation(Type unitType, int unitValue, IFormatProvider formatProvider, [NotNull] string abbreviation)
205+
{
206+
PerformAbbreviationMapping(unitType, unitValue, formatProvider, true, abbreviation);
207+
}
208+
209+
private void PerformAbbreviationMapping(Type unitType, int unitValue, IFormatProvider formatProvider, bool setAsDefault, [NotNull] params string[] abbreviations)
210+
{
138211
if (!unitType.IsEnum())
139212
throw new ArgumentException("Must be an enum type.", nameof(unitType));
140213

@@ -143,15 +216,15 @@ void MapUnitToAbbreviation(Type unitType, int unitValue, IFormatProvider formatP
143216

144217
formatProvider = formatProvider ?? GlobalConfiguration.DefaultCulture;
145218

146-
if(!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider))
219+
if (!_lookupsForCulture.TryGetValue(formatProvider, out var quantitiesForProvider))
147220
quantitiesForProvider = _lookupsForCulture[formatProvider] = new UnitTypeToLookup();
148221

149-
if(!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations))
222+
if (!quantitiesForProvider.TryGetValue(unitType, out var unitToAbbreviations))
150223
unitToAbbreviations = quantitiesForProvider[unitType] = new UnitValueAbbreviationLookup();
151224

152-
foreach(var abbr in abbreviations)
225+
foreach (var abbr in abbreviations)
153226
{
154-
unitToAbbreviations.Add(unitValue, abbr);
227+
unitToAbbreviations.Add(unitValue, abbr, setAsDefault);
155228
}
156229
}
157230

UnitsNet/CustomCode/UnitValueAbbreviationLookup.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,21 @@ internal List<int> GetUnitsForAbbreviation(string abbreviation)
6969
return units.Distinct().ToList();
7070
}
7171

72-
internal void Add(int unit, string abbreviation)
72+
internal void Add(int unit, string abbreviation, bool setAsDefault = false)
7373
{
7474
if(!unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit))
7575
abbreviationsForUnit = unitToAbbreviationMap[unit] = new List<string>();
7676

7777
if(!abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation))
7878
abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List<int>();
7979

80-
abbreviationsForUnit.Add(abbreviation);
80+
abbreviationsForUnit.Remove(abbreviation);
81+
unitsForAbbreviation.Remove(unit);
82+
83+
if (setAsDefault)
84+
abbreviationsForUnit.Insert(0, abbreviation);
85+
else
86+
abbreviationsForUnit.Add(abbreviation);
8187
unitsForAbbreviation.Add(unit);
8288
}
8389
}

0 commit comments

Comments
 (0)