Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions UnitsNet.Tests/UnitSystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ private enum CustomUnit
// ReSharper restore UnusedMember.Local
}

private UnitSystem GetCachedUnitSystem()
{
var cultureInfo = CultureInfo.GetCultureInfo("en-US");
var unitSystem = UnitSystem.GetCached(cultureInfo);
return unitSystem;
}

private static IEnumerable<object> GetUnitTypesWithMissingAbbreviations<TUnit>(string cultureName,
IEnumerable<TUnit> unitValues)
where TUnit : /*Enum constraint hack*/ struct, IComparable, IFormattable
Expand Down Expand Up @@ -161,12 +168,32 @@ public void DecimalPointDigitGroupingCultureFormatting(string culture)
Assert.AreEqual("1.111 m", Length.FromMeters(1111).ToString(LengthUnit.Meter, new CultureInfo(culture)));
}

[Test]
public void Parse_UnambiguousUnitsDoesNotThrow()
{
var unitSystem = GetCachedUnitSystem();
var unit = Volume.Parse("1 l");

Assert.AreEqual(Volume.FromLiters(1), unit);
}

[Test]
public void Parse_AmbiguousUnitsThrowsException()
{
var unitSystem = GetCachedUnitSystem();

// Act 1
Assert.Throws<AmbiguousUnitParseException>( ()=>unitSystem.Parse<VolumeUnit>("tsp"));

// Act 2
Assert.Throws<AmbiguousUnitParseException>(() => Volume.Parse("1 tsp"));
}

[TestCase("m^2", Result = AreaUnit.SquareMeter)]
[TestCase("cm^2", Result = AreaUnit.Undefined)]
public AreaUnit Parse_ReturnsUnitMappedByCustomAbbreviationOrUndefined(string unitAbbreviationToParse)
{
CultureInfo cultureInfo = CultureInfo.GetCultureInfo("en-US");
UnitSystem unitSystem = UnitSystem.GetCached(cultureInfo);
var unitSystem = GetCachedUnitSystem();
unitSystem.MapUnitToAbbreviation(AreaUnit.SquareMeter, "m^2");

return unitSystem.Parse<AreaUnit>(unitAbbreviationToParse);
Expand Down
36 changes: 36 additions & 0 deletions UnitsNet/AmbiguousUnitParseException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright(c) 2007 Andreas Gullberg Larsen
// https://github.com/anjdreas/UnitsNet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;

namespace UnitsNet
{
public class AmbiguousUnitParseException : UnitsNetException
{
public AmbiguousUnitParseException(string message) : base(message)
{
}

public AmbiguousUnitParseException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
70 changes: 53 additions & 17 deletions UnitsNet/CustomCode/UnitSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

// ReSharper disable once CheckNamespace



namespace UnitsNet
{
[PublicAPI]
Expand All @@ -41,7 +43,7 @@ public partial class UnitSystem
/// Per-unit-type dictionary of enum values by abbreviation. This is the inverse of
/// <see cref="_unitTypeToUnitValueToAbbrevs" />.
/// </summary>
private readonly Dictionary<Type, Dictionary<string, int>> _unitTypeToAbbrevToUnitValue;
private readonly Dictionary<Type, AbbreviationMap> _unitTypeToAbbrevToUnitValue;

/// <summary>
/// Per-unit-type dictionary of abbreviations by enum value. This is the inverse of
Expand Down Expand Up @@ -71,7 +73,7 @@ public UnitSystem([CanBeNull] IFormatProvider cultureInfo = null)

Culture = cultureInfo;
_unitTypeToUnitValueToAbbrevs = new Dictionary<Type, Dictionary<int, List<string>>>();
_unitTypeToAbbrevToUnitValue = new Dictionary<Type, Dictionary<string, int>>();
_unitTypeToAbbrevToUnitValue = new Dictionary<Type, AbbreviationMap>();

LoadDefaultAbbreviatons(cultureInfo);
}
Expand Down Expand Up @@ -122,17 +124,33 @@ public TUnit Parse<TUnit>(string unitAbbreviation)
where TUnit : /*Enum constraint hack*/ struct, IComparable, IFormattable
{
Type unitType = typeof (TUnit);
Dictionary<string, int> abbrevToUnitValue;
AbbreviationMap abbrevToUnitValue;
if (!_unitTypeToAbbrevToUnitValue.TryGetValue(unitType, out abbrevToUnitValue))
throw new NotImplementedException(
$"No abbreviations defined for unit type [{unitType}] for culture [{Culture}].");

int unitValue;
TUnit result = abbrevToUnitValue.TryGetValue(unitAbbreviation, out unitValue)
? (TUnit) (object) unitValue
: default(TUnit);
List<int> unitValues;
List<TUnit> units;

return result;
if (abbrevToUnitValue.TryGetValue(unitAbbreviation, out unitValues))
{
units = unitValues.Cast<TUnit>().Distinct().ToList();
}
else
{
units = new List<TUnit>();
}

switch (units.Count)
{
case 1:
return units[0];
case 0:
return default(TUnit);
default:
var unitsCsv = String.Join(", ", units.Select(x => x.ToString()).ToArray());
throw new AmbiguousUnitParseException($"Cannot parse '{unitAbbreviation}' since it could be either of these: {unitsCsv}");
}
}

[PublicAPI]
Expand Down Expand Up @@ -187,15 +205,17 @@ public void MapUnitToAbbreviation(Type unitType, int unitValue, [NotNull] params
unitValueToAbbrev[unitValue] = existingAbbreviations.Concat(abbreviations).Distinct().ToList();
foreach (string abbreviation in abbreviations)
{
Dictionary<string, int> abbrevToUnitValue;
AbbreviationMap abbrevToUnitValue;
if (!_unitTypeToAbbrevToUnitValue.TryGetValue(unitType, out abbrevToUnitValue))
{
abbrevToUnitValue = _unitTypeToAbbrevToUnitValue[unitType] =
new Dictionary<string, int>();
abbrevToUnitValue = _unitTypeToAbbrevToUnitValue[unitType] = new AbbreviationMap();
}

if (!abbrevToUnitValue.ContainsKey(abbreviation))
abbrevToUnitValue[abbreviation] = unitValue;
{
abbrevToUnitValue[abbreviation] = new List<int>();
}
abbrevToUnitValue[abbreviation].Add(unitValue);
}
}

Expand All @@ -205,11 +225,11 @@ public bool TryParse<TUnit>(string unitAbbreviation, out TUnit unit)
{
Type unitType = typeof (TUnit);

Dictionary<string, int> abbrevToUnitValue;
int unitValue;
AbbreviationMap abbrevToUnitValue;
List<int> unitValues;

if (!_unitTypeToAbbrevToUnitValue.TryGetValue(unitType, out abbrevToUnitValue) ||
!abbrevToUnitValue.TryGetValue(unitAbbreviation, out unitValue))
!abbrevToUnitValue.TryGetValue(unitAbbreviation, out unitValues))
{
if (IsDefaultCulture)
{
Expand All @@ -221,8 +241,16 @@ public bool TryParse<TUnit>(string unitAbbreviation, out TUnit unit)
return GetCached(DefaultCulture).TryParse(unitAbbreviation, out unit);
}

unit = (TUnit) (object) unitValue;
return true;
var maps = (List<TUnit>) (object) unitValues;

switch (maps.Count)
{
case 1: unit = maps[0];
return true;
default:
unit = default(TUnit);
return false;
}
}

/// <summary>
Expand Down Expand Up @@ -279,5 +307,13 @@ private void LoadDefaultAbbreviatons([NotNull] IFormatProvider culture)
}
}
}

/// <summary>
/// Avoids having too many nested generics for code clarity
/// </summary>
class AbbreviationMap : Dictionary<string, List<int>>
{

}
}
}
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,11 @@ private static List<Acceleration> ParseWithRegex(string regexString, string str,

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,11 @@ private static List<AmplitudeRatio> ParseWithRegex(string regexString, string st

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Angle.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,11 @@ private static List<Angle> ParseWithRegex(string regexString, string str, IForma

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Area.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,11 @@ private static List<Area> ParseWithRegex(string regexString, string str, IFormat

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Density.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,11 @@ private static List<Density> ParseWithRegex(string regexString, string str, IFor

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Duration.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,11 @@ private static List<Duration> ParseWithRegex(string regexString, string str, IFo

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricCurrent.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,11 @@ private static List<ElectricCurrent> ParseWithRegex(string regexString, string s

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricPotential.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,11 @@ private static List<ElectricPotential> ParseWithRegex(string regexString, string

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricResistance.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,11 @@ private static List<ElectricResistance> ParseWithRegex(string regexString, strin

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Energy.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,11 @@ private static List<Energy> ParseWithRegex(string regexString, string str, IForm

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Flow.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,11 @@ private static List<Flow> ParseWithRegex(string regexString, string str, IFormat

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Force.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,11 @@ private static List<Force> ParseWithRegex(string regexString, string str, IForma

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
6 changes: 5 additions & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ForceChangeRate.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ private static List<ForceChangeRate> ParseWithRegex(string regexString, string s

converted.Add(From(value, unit));
}
catch (Exception ex)
catch(AmbiguousUnitParseException ambiguousException)
{
throw;
}
catch(Exception ex)
{
var newEx = new UnitsNetException("Error parsing string.", ex);
newEx.Data["input"] = str;
Expand Down
Loading