Skip to content

Bugfix/parse mapped abbrev #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 30, 2017
Merged
Show file tree
Hide file tree
Changes from 15 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
49 changes: 49 additions & 0 deletions UnitsNet.Tests/CustomCode/ParseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,55 @@ public void TryParseLengthUnitUsEnglish(string s, bool expected)
Assert.Equal(expected, actual);
}

[Theory]
[InlineData("!")]
[InlineData("@")]
[InlineData("#")]
[InlineData("$")]
[InlineData("%")]
[InlineData("^")]
[InlineData("&")]
[InlineData("*")]
[InlineData("-")]
[InlineData("_")]
[InlineData("?")]
[InlineData("123")]
[InlineData(" ")]
public void TryParseLengthUnitAbbreviationSpecialCharacters(string s)
{
UnitSystem unitSystem = UnitSystem.GetCached("en-US");
string abbrev = $"m{s}s";
unitSystem.MapUnitToAbbreviation(UnitsNet.Units.LengthUnit.Meter, abbrev);
UnitsNet.Units.LengthUnit result;
bool actual = unitSystem.TryParse<UnitsNet.Units.LengthUnit>(abbrev, out result);
Assert.Equal(true, actual);
Assert.Equal(UnitsNet.Units.LengthUnit.Meter, result);
}

[Theory]
[InlineData("!")]
[InlineData("@")]
[InlineData("#")]
[InlineData("$")]
[InlineData("%")]
[InlineData("^")]
[InlineData("&")]
[InlineData("*")]
[InlineData("-")]
[InlineData("_")]
[InlineData("?")]
[InlineData("123")]
[InlineData(" ")]
public void TryParseLengthSpecialCharacters(string s)
{
UnitSystem unitSystem = UnitSystem.GetCached("en-US");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized a better approach than using UnitSystem.GetCached() is to add a new ctor overload that allows you to disable default unit abbreviations from being loaded. I just added this in bf11a78. GetCached() is not ideal, because it is

  1. Statically cached (problem if running tests in parallel) and it
  2. Automatically loads all the units defined in .json

If Length.json already contained these units you are defining, you don't know for sure if the test passes due to calling MapUnitToAbbreviation or not.

So I would change this snippet to:

var unitSystem = new UnitSystem("en-US", loadDefaultAbbreviations: false);
unitSystem.MapUnitToAbbreviation(UnitsNet.Units.LengthUnit.Meter, "m2");

string abbrev = $"m{s}s";
unitSystem.MapUnitToAbbreviation(UnitsNet.Units.LengthUnit.Meter, abbrev);
bool actual = Length.TryParse($"10 {abbrev}", out Length result);
Assert.Equal(true, actual);
Assert.Equal(Length.FromMeters(10d), result);
}

private static string AssertExceptionAndGetFullTypeName(Action code)
{
var exception = Assert.ThrowsAny<Exception>(code);
Expand Down
28 changes: 18 additions & 10 deletions UnitsNet/CustomCode/UnitParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@

namespace UnitsNet
{
internal delegate TUnit ParseUnit<out TUnit>(string value, string unit, IFormatProvider formatProvider = null);
internal delegate TQuantity ParseUnit<out TQuantity>(string value, string unit, IFormatProvider formatProvider = null);

internal static class UnitParser
{
[SuppressMessage("ReSharper", "UseStringInterpolation")]
internal static TUnit ParseUnit<TUnit>([NotNull] string str,
internal static TQuantity ParseUnit<TQuantityEnum, TQuantity>([NotNull] string str,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed the type parameters to match a naming convention.
Instead of TQuantity, TUnit it is now TQuantityEnum, TQuantity.

[CanBeNull] IFormatProvider formatProvider,
[NotNull] ParseUnit<TUnit> parseUnit,
[NotNull] Func<TUnit, TUnit, TUnit> add)
[NotNull] ParseUnit<TQuantity> parseUnit,
[NotNull] Func<TQuantity, TQuantity, TQuantity> add)
{
if (str == null) throw new ArgumentNullException(nameof(str));
if (parseUnit == null) throw new ArgumentNullException(nameof(parseUnit));
Expand All @@ -54,15 +54,23 @@ internal static TUnit ParseUnit<TUnit>([NotNull] string str,

const string exponentialRegex = @"(?:[eE][-+]?\d+)?)";

string[] unitAbbreviations = UnitSystem.GetCached(formatProvider)
.GetAllAbbreviations(typeof(TQuantityEnum))
.OrderByDescending(s => s.Length) // Important to order by length -- if "m" is before "mm" and the input is "mm", it will match just "m" and throw invalid string error
.Select(Regex.Escape) // Escape special regex characters
.ToArray();

string unitsRegex = $"({String.Join("|", unitAbbreviations)})";

string regexString = string.Format(@"(?:\s*(?<value>[-+]?{0}{1}{2}{3})?{4}{5}",
numRegex, // capture base (integral) Quantity value
exponentialRegex, // capture exponential (if any), end of Quantity capturing
@"\s?", // ignore whitespace (allows both "1kg", "1 kg")
@"(?<unit>[^\s\d,]+)", // capture Unit (non-whitespace) input
$@"(?<unit>{unitsRegex})", // capture Unit by list of abbreviations
@"(and)?,?", // allow "and" & "," separators between quantities
@"(?<invalid>[a-z]*)?"); // capture invalid input

List<TUnit> quantities = ParseWithRegex(regexString, str, parseUnit, formatProvider);
List<TQuantity> quantities = ParseWithRegex(regexString, str, parseUnit, formatProvider);
if (quantities.Count == 0)
{
throw new ArgumentException(
Expand All @@ -74,14 +82,14 @@ internal static TUnit ParseUnit<TUnit>([NotNull] string str,

/// <summary>
/// Parse a string given a particular regular expression.
/// </summary>
/// </summary>
/// <exception cref="UnitsNetException">Error parsing string.</exception>
private static List<TUnit> ParseWithRegex<TUnit>(string regexString, string str, ParseUnit<TUnit> parseUnit,
private static List<TQuantity> ParseWithRegex<TQuantity>(string regexString, string str, ParseUnit<TQuantity> parseUnit,
IFormatProvider formatProvider = null)
{
var regex = new Regex(regexString);
MatchCollection matches = regex.Matches(str.Trim());
var converted = new List<TUnit>();
var converted = new List<TQuantity>();

foreach (Match match in matches)
{
Expand Down Expand Up @@ -121,4 +129,4 @@ private static List<TUnit> ParseWithRegex<TUnit>(string regexString, string str,
return converted;
}
}
}
}
23 changes: 23 additions & 0 deletions UnitsNet/CustomCode/UnitSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,29 @@ public string[] GetAllAbbreviations(Type unitType, int unitValue)
: GetCached(FallbackCulture).GetAllAbbreviations(unitType, unitValue);
}

/// <summary>
/// Get all abbreviations for unit.
/// </summary>
/// <param name="unitType">Enum type for unit.</param>
/// <param name="unitValue">Enum value for unit.</param>
/// <returns>Unit abbreviations associated with unit.</returns>
[PublicAPI]
public string[] GetAllAbbreviations(Type unitType)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat, this method has been requested before.

{
Dictionary<int, List<string>> unitValueToAbbrevs;
List<string> abbrevs = new List<string>();

if (_unitTypeToUnitValueToAbbrevs.TryGetValue(unitType, out unitValueToAbbrevs))
{
return unitValueToAbbrevs.Values.SelectMany(x => x).ToArray();
}

// Fall back to default culture
return IsFallbackCulture
? new[] {$"(no abbreviations for {unitType.Name})"}
: GetCached(FallbackCulture).GetAllAbbreviations(unitType);
}

private void LoadDefaultAbbreviations([NotNull] IFormatProvider culture)
{
foreach (UnitLocalization localization in DefaultLocalizations)
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ public static Acceleration Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Acceleration>(str, formatProvider,
return UnitParser.ParseUnit<AccelerationUnit, Acceleration>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ public static AmplitudeRatio Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<AmplitudeRatio>(str, formatProvider,
return UnitParser.ParseUnit<AmplitudeRatioUnit, AmplitudeRatio>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Angle.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ public static Angle Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Angle>(str, formatProvider,
return UnitParser.ParseUnit<AngleUnit, Angle>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ApparentPower.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ public static ApparentPower Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ApparentPower>(str, formatProvider,
return UnitParser.ParseUnit<ApparentPowerUnit, ApparentPower>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Area.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ public static Area Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Area>(str, formatProvider,
return UnitParser.ParseUnit<AreaUnit, Area>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ public static BrakeSpecificFuelConsumption Parse(string str, [CanBeNull] Culture
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<BrakeSpecificFuelConsumption>(str, formatProvider,
return UnitParser.ParseUnit<BrakeSpecificFuelConsumptionUnit, BrakeSpecificFuelConsumption>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Density.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1683,7 +1683,7 @@ public static Density Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Density>(str, formatProvider,
return UnitParser.ParseUnit<DensityUnit, Density>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Duration.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ public static Duration Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Duration>(str, formatProvider,
return UnitParser.ParseUnit<DurationUnit, Duration>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/DynamicViscosity.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ public static DynamicViscosity Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<DynamicViscosity>(str, formatProvider,
return UnitParser.ParseUnit<DynamicViscosityUnit, DynamicViscosity>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricAdmittance.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ public static ElectricAdmittance Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ElectricAdmittance>(str, formatProvider,
return UnitParser.ParseUnit<ElectricAdmittanceUnit, ElectricAdmittance>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricCurrent.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ public static ElectricCurrent Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ElectricCurrent>(str, formatProvider,
return UnitParser.ParseUnit<ElectricCurrentUnit, ElectricCurrent>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricPotential.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ public static ElectricPotential Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ElectricPotential>(str, formatProvider,
return UnitParser.ParseUnit<ElectricPotentialUnit, ElectricPotential>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ public static ElectricPotentialAc Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ElectricPotentialAc>(str, formatProvider,
return UnitParser.ParseUnit<ElectricPotentialAcUnit, ElectricPotentialAc>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ public static ElectricPotentialDc Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ElectricPotentialDc>(str, formatProvider,
return UnitParser.ParseUnit<ElectricPotentialDcUnit, ElectricPotentialDc>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ElectricResistance.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ public static ElectricResistance Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ElectricResistance>(str, formatProvider,
return UnitParser.ParseUnit<ElectricResistanceUnit, ElectricResistance>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Energy.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ public static Energy Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Energy>(str, formatProvider,
return UnitParser.ParseUnit<EnergyUnit, Energy>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Flow.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ public static Flow Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Flow>(str, formatProvider,
return UnitParser.ParseUnit<FlowUnit, Flow>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Force.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ public static Force Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Force>(str, formatProvider,
return UnitParser.ParseUnit<ForceUnit, Force>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ForceChangeRate.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ public static ForceChangeRate Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ForceChangeRate>(str, formatProvider,
return UnitParser.ParseUnit<ForceChangeRateUnit, ForceChangeRate>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/ForcePerLength.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ public static ForcePerLength Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<ForcePerLength>(str, formatProvider,
return UnitParser.ParseUnit<ForcePerLengthUnit, ForcePerLength>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Frequency.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ public static Frequency Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Frequency>(str, formatProvider,
return UnitParser.ParseUnit<FrequencyUnit, Frequency>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Information.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,7 +1350,7 @@ public static Information Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Information>(str, formatProvider,
return UnitParser.ParseUnit<InformationUnit, Information>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/KinematicViscosity.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ public static KinematicViscosity Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<KinematicViscosity>(str, formatProvider,
return UnitParser.ParseUnit<KinematicViscosityUnit, KinematicViscosity>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Length.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ public static Length Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Length>(str, formatProvider,
return UnitParser.ParseUnit<LengthUnit, Length>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
2 changes: 1 addition & 1 deletion UnitsNet/GeneratedCode/UnitClasses/Level.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public static Level Parse(string str, [CanBeNull] Culture culture)
#else
IFormatProvider formatProvider = culture;
#endif
return UnitParser.ParseUnit<Level>(str, formatProvider,
return UnitParser.ParseUnit<LevelUnit, Level>(str, formatProvider,
delegate(string value, string unit, IFormatProvider formatProvider2)
{
double parsedValue = double.Parse(value, formatProvider2);
Expand Down
Loading