diff --git a/Build/build-functions.psm1 b/Build/build-functions.psm1 index 775db15349..9dcc3b281b 100644 --- a/Build/build-functions.psm1 +++ b/Build/build-functions.psm1 @@ -18,24 +18,9 @@ function Remove-ArtifactsDir { } function Update-GeneratedCode { - # Regenerate source code since it occasionally happens that merged pull requests did not include all the regenerated code - $genScriptDotNet = "$root/UnitsNet/Scripts/GenerateUnits.ps1" - $genScriptWrc = "$root/UnitsNet.WindowsRuntimeComponent/Scripts/GenerateUnits.ps1" - - write-host -foreground blue "Generate code for .NET...`n---" - write-host $genScriptDotNet - & $genScriptDotNet - if ($lastexitcode -ne 0) { exit 1 } - - # Regenerate WRC code even if we are not building that target. - # The reason is that build.bat will skip WRC build since most people don't have that dependency installed. - # AppVeyor build server would still regen and build WRC regardless, but this way we also get the changes - # into pull requests so they are visible and master branch is kept up-to-date. - write-host -foreground blue "Generate code for Windows Runtime Component...`n---" - write-host $genScriptWrc - & $genScriptWrc + write-host -foreground blue "Generate code...`n---" + dotnet run --project "$root/CodeGen" if ($lastexitcode -ne 0) { exit 1 } - write-host -foreground blue "Generate code...END`n" } diff --git a/CodeGen/CodeGen.csproj b/CodeGen/CodeGen.csproj new file mode 100644 index 0000000000..8fd50fc3a6 --- /dev/null +++ b/CodeGen/CodeGen.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp2.1 + + + + + + + + + + diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateQuantityTypeSourceCode.ps1 b/CodeGen/Generators/GeneratorBase.cs similarity index 56% rename from UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateQuantityTypeSourceCode.ps1 rename to CodeGen/Generators/GeneratorBase.cs index 7aeae8432a..53c9a23f7f 100644 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateQuantityTypeSourceCode.ps1 +++ b/CodeGen/Generators/GeneratorBase.cs @@ -1,7 +1,16 @@ -function GenerateQuantityTypeSourceCode($quantities) +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using CodeGen.Helpers; + +namespace CodeGen.Generators { -@" -//------------------------------------------------------------------------------ + internal abstract class GeneratorBase + { + protected readonly MyTextWriter Writer = new MyTextWriter(); + public abstract string Generate(); + + public const string GeneratedFileHeader = @"//------------------------------------------------------------------------------ // // This code was generated by \generate-code.bat. // @@ -19,24 +28,6 @@ // Licensed under MIT No Attribution, see LICENSE file at the root. // Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -// ReSharper disable once CheckNamespace -namespace UnitsNet -{ - /// - /// Lists all generated quantities with the same name as the quantity struct type, - /// such as Length, Mass, Force etc. - /// This is useful for populating options in the UI, such as creating a generic conversion - /// tool with inputValue, quantityName, fromUnit and toUnit selectors. - /// - public enum QuantityType - { - Undefined = 0, -"@; foreach ($quantity in $quantities) { -@" - $($quantity.Name), -"@; }@" +"; } } -"@; -} diff --git a/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs b/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs new file mode 100644 index 0000000000..1d8692fe51 --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs @@ -0,0 +1,1134 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using CodeGen.Helpers; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class QuantityGenerator : GeneratorBase + { + private readonly Quantity _quantity; + + private readonly bool _isDimensionless; + private readonly string _unitEnumName; + private readonly string _valueType; + private readonly Unit _baseUnit; + + public QuantityGenerator(Quantity quantity) + { + _quantity = quantity ?? throw new ArgumentNullException(nameof(quantity)); + + _baseUnit = quantity.Units.FirstOrDefault(u => u.SingularName == _quantity.BaseUnit) ?? + throw new ArgumentException($"No unit found with SingularName equal to BaseUnit [{_quantity.BaseUnit}]. This unit must be defined.", + nameof(quantity)); + + _valueType = quantity.BaseType; + _unitEnumName = $"{quantity.Name}Unit"; + + BaseDimensions baseDimensions = quantity.BaseDimensions; + _isDimensionless = baseDimensions == null || + baseDimensions.L == 0 && + baseDimensions.M == 0 && + baseDimensions.T == 0 && + baseDimensions.I == 0 && + baseDimensions.Θ == 0 && + baseDimensions.N == 0 && + baseDimensions.J == 0; + + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +using System; +using System.Globalization; +using System.Linq; +using JetBrains.Annotations; +using UnitsNet.InternalHelpers; +using UnitsNet.Units; + +// ReSharper disable once CheckNamespace + +namespace UnitsNet +{"); + Writer.WLIfText(1, GetObsoleteAttributeOrNull(_quantity)); + Writer.WL($@" + /// + /// + /// {_quantity.XmlDoc} + /// "); + + Writer.WLCondition(_quantity.XmlDocRemarks.HasText(), $@" + /// + /// {_quantity.XmlDocRemarks} + /// "); + + Writer.WL($@" + public partial struct {_quantity.Name} : IQuantity<{_unitEnumName}>, IEquatable<{_quantity.Name}>, IComparable, IComparable<{_quantity.Name}>, IConvertible, IFormattable + {{ + /// + /// The numeric value this quantity was constructed with. + /// + private readonly {_quantity.BaseType} _value; + + /// + /// The unit this quantity was constructed with. + /// + private readonly {_unitEnumName}? _unit; +"); + GenerateStaticConstructor(); + GenerateInstanceConstructors(); + GenerateStaticProperties(); + GenerateProperties(); + GenerateConversionProperties(); + GenerateStaticMethods(); + GenerateStaticFactoryMethods(); + GenerateStaticParseMethods(); + GenerateArithmeticOperators(); + GenerateEqualityAndComparison(); + GenerateConversionMethods(); + GenerateToString(); + GenerateIConvertibleMethods(); + + Writer.WL($@" + }} +}}"); + return Writer.ToString(); + } + + private void GenerateStaticConstructor() + { + var baseDimensions = _quantity.BaseDimensions; + Writer.WL($@" + static {_quantity.Name}() + {{"); + Writer.WL(_isDimensionless + ? @" + BaseDimensions = BaseDimensions.Dimensionless; +" + : $@" + BaseDimensions = new BaseDimensions({baseDimensions.L}, {baseDimensions.M}, {baseDimensions.T}, {baseDimensions.I}, {baseDimensions.Θ}, {baseDimensions.N}, {baseDimensions.J}); +"); + + Writer.WL($@" + Info = new QuantityInfo<{_unitEnumName}>(QuantityType.{_quantity.Name}, + new UnitInfo<{_unitEnumName}>[] {{"); + + foreach (var unit in _quantity.Units) + { + var baseUnits = unit.BaseUnits; + if (baseUnits == null) + { + Writer.WL($@" + new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, BaseUnits.Undefined),"); + } + else + { + var baseUnitsCtorArgs = string.Join(", ", + new[] + { + baseUnits.L != null ? $"length: LengthUnit.{baseUnits.L}" : null, + baseUnits.M != null ? $"mass: MassUnit.{baseUnits.M}" : null, + baseUnits.T != null ? $"time: DurationUnit.{baseUnits.T}" : null, + baseUnits.I != null ? $"current: ElectricCurrentUnit.{baseUnits.I}" : null, + baseUnits.Θ != null ? $"temperature: TemperatureUnit.{baseUnits.Θ}" : null, + baseUnits.N != null ? $"amount: AmountOfSubstanceUnit.{baseUnits.N}" : null, + baseUnits.J != null ? $"luminousIntensity: LuminousIntensityUnit.{baseUnits.J}" : null + }.Where(str => str != null)); + + Writer.WL($@" + new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, new BaseUnits({baseUnitsCtorArgs})),"); + } + } + + Writer.WL(@" + }, + BaseUnit, Zero, BaseDimensions); + } +"); + } + + private void GenerateInstanceConstructors() + { + Writer.WL($@" + /// + /// Creates the quantity with the given numeric value and unit. + /// + /// The numeric value to contruct this quantity with. + /// The unit representation to contruct this quantity with. + /// If value is NaN or Infinity. + public {_quantity.Name}({_quantity.BaseType} numericValue, {_unitEnumName} unit) + {{ + if(unit == {_unitEnumName}.Undefined) + throw new ArgumentException(""The quantity can not be created with an undefined unit."", nameof(unit)); +"); + + Writer.WL(_quantity.BaseType == "double" + ? @" + _value = Guard.EnsureValidNumber(numericValue, nameof(numericValue));" + : @" + _value = numericValue;"); + Writer.WL($@" + _unit = unit; + }} + + /// + /// Creates an instance of the quantity with the given numeric value in units compatible with the given . + /// If multiple compatible units were found, the first match is used. + /// + /// The numeric value to contruct this quantity with. + /// The unit system to create the quantity with. + /// The given is null. + /// No unit was found for the given . + public {_quantity.Name}({_valueType} numericValue, UnitSystem unitSystem) + {{ + if(unitSystem == null) throw new ArgumentNullException(nameof(unitSystem)); + + var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits); + var firstUnitInfo = unitInfos.FirstOrDefault(); +"); + + Writer.WL(_quantity.BaseType == "double" + ? @" + _value = Guard.EnsureValidNumber(numericValue, nameof(numericValue));" + : @" + _value = numericValue;"); + Writer.WL(@" + _unit = firstUnitInfo?.Value ?? throw new ArgumentException(""No units were found for the given UnitSystem."", nameof(unitSystem)); + } +"); + } + + private void GenerateStaticProperties() + { + Writer.WL($@" + #region Static Properties + + /// + public static QuantityInfo<{_unitEnumName}> Info {{ get; }} + + /// + /// The of this quantity. + /// + public static BaseDimensions BaseDimensions {{ get; }} + + /// + /// The base unit of {_quantity.Name}, which is {_quantity.BaseUnit}. All conversions go via this value. + /// + public static {_unitEnumName} BaseUnit {{ get; }} = {_unitEnumName}.{_quantity.BaseUnit}; + + /// + /// Represents the largest possible value of {_quantity.Name} + /// + public static {_quantity.Name} MaxValue {{ get; }} = new {_quantity.Name}({_valueType}.MaxValue, BaseUnit); + + /// + /// Represents the smallest possible value of {_quantity.Name} + /// + public static {_quantity.Name} MinValue {{ get; }} = new {_quantity.Name}({_valueType}.MinValue, BaseUnit); + + /// + /// The of this quantity. + /// + public static QuantityType QuantityType {{ get; }} = QuantityType.{_quantity.Name}; + + /// + /// All units of measurement for the {_quantity.Name} quantity. + /// + public static {_unitEnumName}[] Units {{ get; }} = Enum.GetValues(typeof({_unitEnumName})).Cast<{_unitEnumName}>().Except(new {_unitEnumName}[]{{ {_unitEnumName}.Undefined }}).ToArray(); + + /// + /// Gets an instance of this quantity with a value of 0 in the base unit {_quantity.BaseUnit}. + /// + public static {_quantity.Name} Zero {{ get; }} = new {_quantity.Name}(0, BaseUnit); + + #endregion +"); + } + + private void GenerateProperties() + { + Writer.WL($@" + #region Properties + + /// + /// The numeric value this quantity was constructed with. + /// + public {_valueType} Value => _value; +"); + + // Need to provide explicit interface implementation for decimal quantities like Information + if (_quantity.BaseType != "double") + Writer.WL(@" + double IQuantity.Value => (double) _value; +"); + + Writer.WL($@" + Enum IQuantity.Unit => Unit; + + /// + public {_unitEnumName} Unit => _unit.GetValueOrDefault(BaseUnit); + + /// + public QuantityInfo<{_unitEnumName}> QuantityInfo => Info; + + /// + QuantityInfo IQuantity.QuantityInfo => Info; + + /// + /// The of this quantity. + /// + public QuantityType Type => {_quantity.Name}.QuantityType; + + /// + /// The of this quantity. + /// + public BaseDimensions Dimensions => {_quantity.Name}.BaseDimensions; + + #endregion +"); + } + + private void GenerateConversionProperties() + { + Writer.WL(@" + #region Conversion Properties +"); + foreach (var unit in _quantity.Units) + { + Writer.WL($@" + /// + /// Get {_quantity.Name} in {unit.PluralName}. + /// "); + Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit)); + Writer.WL($@" + public double {unit.PluralName} => As({_unitEnumName}.{unit.SingularName}); +"); + } + + Writer.WL(@" + + #endregion +"); + } + + private void GenerateStaticMethods() + { + Writer.WL($@" + + #region Static Methods + + /// + /// Get unit abbreviation string. + /// + /// Unit to get abbreviation for. + /// Unit abbreviation string. + public static string GetAbbreviation({_unitEnumName} unit) + {{ + return GetAbbreviation(unit, null); + }} + + /// + /// Get unit abbreviation string. + /// + /// Unit to get abbreviation for. + /// Unit abbreviation string. + /// Format to use for localization. Defaults to if null. + public static string GetAbbreviation({_unitEnumName} unit, [CanBeNull] IFormatProvider provider) + {{ + return UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit, provider); + }} + + #endregion +"); + } + + private void GenerateStaticFactoryMethods() + { + Writer.WL(@" + #region Static Factory Methods +"); + foreach (var unit in _quantity.Units) + { + var valueParamName = unit.PluralName.ToLowerInvariant(); + Writer.WL($@" + /// + /// Get {_quantity.Name} from {unit.PluralName}. + /// + /// If value is NaN or Infinity."); + Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit)); + Writer.WL($@" + public static {_quantity.Name} From{unit.PluralName}(QuantityValue {valueParamName}) + {{ + {_valueType} value = ({_valueType}) {valueParamName}; + return new {_quantity.Name}(value, {_unitEnumName}.{unit.SingularName}); + }}"); + } + + Writer.WL(); + Writer.WL($@" + /// + /// Dynamically convert from value and unit enum to . + /// + /// Value to convert from. + /// Unit to convert from. + /// {_quantity.Name} unit value. + public static {_quantity.Name} From(QuantityValue value, {_unitEnumName} fromUnit) + {{ + return new {_quantity.Name}(({_valueType})value, fromUnit); + }} + + #endregion +"); + } + + private void GenerateStaticParseMethods() + { + Writer.WL($@" + #region Static Parse Methods + + /// + /// Parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// + /// Expected string to have one or two pairs of quantity and unit in the format + /// ""<quantity> <unit>"". Eg. ""5.5 m"" or ""1ft 2in"" + /// + /// + /// More than one unit is represented by the specified unit abbreviation. + /// Example: Volume.Parse(""1 cup"") will throw, because it can refer to any of + /// , and . + /// + /// + /// If anything else goes wrong, typically due to a bug or unhandled case. + /// We wrap exceptions in to allow you to distinguish + /// Units.NET exceptions from other exceptions. + /// + public static {_quantity.Name} Parse(string str) + {{ + return Parse(str, null); + }} + + /// + /// Parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// + /// Expected string to have one or two pairs of quantity and unit in the format + /// ""<quantity> <unit>"". Eg. ""5.5 m"" or ""1ft 2in"" + /// + /// + /// More than one unit is represented by the specified unit abbreviation. + /// Example: Volume.Parse(""1 cup"") will throw, because it can refer to any of + /// , and . + /// + /// + /// If anything else goes wrong, typically due to a bug or unhandled case. + /// We wrap exceptions in to allow you to distinguish + /// Units.NET exceptions from other exceptions. + /// + /// Format to use when parsing number and unit. Defaults to if null. + public static {_quantity.Name} Parse(string str, [CanBeNull] IFormatProvider provider) + {{ + return QuantityParser.Default.Parse<{_quantity.Name}, {_unitEnumName}>( + str, + provider, + From); + }} + + /// + /// Try to parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// Resulting unit quantity if successful. + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + public static bool TryParse([CanBeNull] string str, out {_quantity.Name} result) + {{ + return TryParse(str, null, out result); + }} + + /// + /// Try to parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// Resulting unit quantity if successful. + /// True if successful, otherwise false. + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + /// Format to use when parsing number and unit. Defaults to if null. + public static bool TryParse([CanBeNull] string str, [CanBeNull] IFormatProvider provider, out {_quantity.Name} result) + {{ + return QuantityParser.Default.TryParse<{_quantity.Name}, {_unitEnumName}>( + str, + provider, + From, + out result); + }} + + /// + /// Parse a unit string. + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.ParseUnit(""m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// Error parsing string. + public static {_unitEnumName} ParseUnit(string str) + {{ + return ParseUnit(str, null); + }} + + /// + /// Parse a unit string. + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.ParseUnit(""m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// Error parsing string. + /// Format to use when parsing number and unit. Defaults to if null. + public static {_unitEnumName} ParseUnit(string str, IFormatProvider provider = null) + {{ + return UnitParser.Default.Parse<{_unitEnumName}>(str, provider); + }} + + /// + public static bool TryParseUnit(string str, out {_unitEnumName} unit) + {{ + return TryParseUnit(str, null, out unit); + }} + + /// + /// Parse a unit string. + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// The parsed unit if successful. + /// True if successful, otherwise false. + /// + /// Length.TryParseUnit(""m"", new CultureInfo(""en-US"")); + /// + /// Format to use when parsing number and unit. Defaults to if null. + public static bool TryParseUnit(string str, IFormatProvider provider, out {_unitEnumName} unit) + {{ + return UnitParser.Default.TryParse<{_unitEnumName}>(str, provider, out unit); + }} + + #endregion +"); + } + + private void GenerateArithmeticOperators() + { + if (!_quantity.GenerateArithmetic) + return; + + // Logarithmic units required different arithmetic + if (_quantity.Logarithmic) + { + GenerateLogarithmicArithmeticOperators(); + return; + } + + Writer.WL($@" + #region Arithmetic Operators + + /// Negate the value. + public static {_quantity.Name} operator -({_quantity.Name} right) + {{ + return new {_quantity.Name}(-right.Value, right.Unit); + }} + + /// Get from adding two . + public static {_quantity.Name} operator +({_quantity.Name} left, {_quantity.Name} right) + {{ + return new {_quantity.Name}(left.Value + right.GetValueAs(left.Unit), left.Unit); + }} + + /// Get from subtracting two . + public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right) + {{ + return new {_quantity.Name}(left.Value - right.GetValueAs(left.Unit), left.Unit); + }} + + /// Get from multiplying value and . + public static {_quantity.Name} operator *({_valueType} left, {_quantity.Name} right) + {{ + return new {_quantity.Name}(left * right.Value, right.Unit); + }} + + /// Get from multiplying value and . + public static {_quantity.Name} operator *({_quantity.Name} left, {_valueType} right) + {{ + return new {_quantity.Name}(left.Value * right, left.Unit); + }} + + /// Get from dividing by value. + public static {_quantity.Name} operator /({_quantity.Name} left, {_valueType} right) + {{ + return new {_quantity.Name}(left.Value / right, left.Unit); + }} + + /// Get ratio value from dividing by . + public static double operator /({_quantity.Name} left, {_quantity.Name} right) + {{ + return left.{_baseUnit.PluralName} / right.{_baseUnit.PluralName}; + }} + + #endregion +"); + } + + private void GenerateLogarithmicArithmeticOperators() + { + var scalingFactor = _quantity.LogarithmicScalingFactor; + // Most logarithmic operators need a simple scaling factor of 10. However, certain units such as voltage ratio need to use 20 instead. + var x = (10 * scalingFactor).ToString(); + Writer.WL($@" + #region Logarithmic Arithmetic Operators + + /// Negate the value. + public static {_quantity.Name} operator -({_quantity.Name} right) + {{ + return new {_quantity.Name}(-right.Value, right.Unit); + }} + + /// Get from logarithmic addition of two . + public static {_quantity.Name} operator +({_quantity.Name} left, {_quantity.Name} right) + {{ + // Logarithmic addition + // Formula: {x}*log10(10^(x/{x}) + 10^(y/{x})) + return new {_quantity.Name}({x}*Math.Log10(Math.Pow(10, left.Value/{x}) + Math.Pow(10, right.GetValueAs(left.Unit)/{x})), left.Unit); + }} + + /// Get from logarithmic subtraction of two . + public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right) + {{ + // Logarithmic subtraction + // Formula: {x}*log10(10^(x/{x}) - 10^(y/{x})) + return new {_quantity.Name}({x}*Math.Log10(Math.Pow(10, left.Value/{x}) - Math.Pow(10, right.GetValueAs(left.Unit)/{x})), left.Unit); + }} + + /// Get from logarithmic multiplication of value and . + public static {_quantity.Name} operator *({_valueType} left, {_quantity.Name} right) + {{ + // Logarithmic multiplication = addition + return new {_quantity.Name}(left + right.Value, right.Unit); + }} + + /// Get from logarithmic multiplication of value and . + public static {_quantity.Name} operator *({_quantity.Name} left, double right) + {{ + // Logarithmic multiplication = addition + return new {_quantity.Name}(left.Value + ({_valueType})right, left.Unit); + }} + + /// Get from logarithmic division of by value. + public static {_quantity.Name} operator /({_quantity.Name} left, double right) + {{ + // Logarithmic division = subtraction + return new {_quantity.Name}(left.Value - ({_valueType})right, left.Unit); + }} + + /// Get ratio value from logarithmic division of by . + public static double operator /({_quantity.Name} left, {_quantity.Name} right) + {{ + // Logarithmic division = subtraction + return Convert.ToDouble(left.Value - right.GetValueAs(left.Unit)); + }} + + #endregion +"); + } + + private void GenerateEqualityAndComparison() + { + Writer.WL($@" + #region Equality / IComparable + + /// Returns true if less or equal to. + public static bool operator <=({_quantity.Name} left, {_quantity.Name} right) + {{ + return left.Value <= right.GetValueAs(left.Unit); + }} + + /// Returns true if greater than or equal to. + public static bool operator >=({_quantity.Name} left, {_quantity.Name} right) + {{ + return left.Value >= right.GetValueAs(left.Unit); + }} + + /// Returns true if less than. + public static bool operator <({_quantity.Name} left, {_quantity.Name} right) + {{ + return left.Value < right.GetValueAs(left.Unit); + }} + + /// Returns true if greater than. + public static bool operator >({_quantity.Name} left, {_quantity.Name} right) + {{ + return left.Value > right.GetValueAs(left.Unit); + }} + + /// Returns true if exactly equal. + /// Consider using for safely comparing floating point values. + public static bool operator ==({_quantity.Name} left, {_quantity.Name} right) + {{ + return left.Equals(right); + }} + + /// Returns true if not exactly equal. + /// Consider using for safely comparing floating point values. + public static bool operator !=({_quantity.Name} left, {_quantity.Name} right) + {{ + return !(left == right); + }} + + /// + public int CompareTo(object obj) + {{ + if(obj is null) throw new ArgumentNullException(nameof(obj)); + if(!(obj is {_quantity.Name} obj{_quantity.Name})) throw new ArgumentException(""Expected type {_quantity.Name}."", nameof(obj)); + + return CompareTo(obj{_quantity.Name}); + }} + + /// + public int CompareTo({_quantity.Name} other) + {{ + return _value.CompareTo(other.GetValueAs(this.Unit)); + }} + + /// + /// Consider using for safely comparing floating point values. + public override bool Equals(object obj) + {{ + if(obj is null || !(obj is {_quantity.Name} obj{_quantity.Name})) + return false; + + return Equals(obj{_quantity.Name}); + }} + + /// + /// Consider using for safely comparing floating point values. + public bool Equals({_quantity.Name} other) + {{ + return _value.Equals(other.GetValueAs(this.Unit)); + }} + + /// + /// + /// Compare equality to another {_quantity.Name} within the given absolute or relative tolerance. + /// + /// + /// Relative tolerance is defined as the maximum allowable absolute difference between this quantity's value and + /// as a percentage of this quantity's value. will be converted into + /// this quantity's unit for comparison. A relative tolerance of 0.01 means the absolute difference must be within +/- 1% of + /// this quantity's value to be considered equal. + /// + /// In this example, the two quantities will be equal if the value of b is within +/- 1% of a (0.02m or 2cm). + /// + /// var a = Length.FromMeters(2.0); + /// var b = Length.FromInches(50.0); + /// a.Equals(b, 0.01, ComparisonType.Relative); + /// + /// + /// + /// + /// Absolute tolerance is defined as the maximum allowable absolute difference between this quantity's value and + /// as a fixed number in this quantity's unit. will be converted into + /// this quantity's unit for comparison. + /// + /// In this example, the two quantities will be equal if the value of b is within 0.01 of a (0.01m or 1cm). + /// + /// var a = Length.FromMeters(2.0); + /// var b = Length.FromInches(50.0); + /// a.Equals(b, 0.01, ComparisonType.Absolute); + /// + /// + /// + /// + /// Note that it is advised against specifying zero difference, due to the nature + /// of floating point operations and using System.Double internally. + /// + /// + /// The other quantity to compare to. + /// The absolute or relative tolerance value. Must be greater than or equal to 0. + /// The comparison type: either relative or absolute. + /// True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance. + public bool Equals({_quantity.Name} other, double tolerance, ComparisonType comparisonType) + {{ + if(tolerance < 0) + throw new ArgumentOutOfRangeException(""tolerance"", ""Tolerance must be greater than or equal to 0.""); + + double thisValue = (double)this.Value; + double otherValueInThisUnits = other.As(this.Unit); + + return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType); + }} + + /// + /// Returns the hash code for this instance. + /// + /// A hash code for the current {_quantity.Name}. + public override int GetHashCode() + {{ + return new {{ QuantityType, Value, Unit }}.GetHashCode(); + }} + + #endregion +"); + } + + private void GenerateConversionMethods() + { + Writer.WL($@" + #region Conversion Methods + + /// + /// Convert to the unit representation . + /// + /// Value converted to the specified unit. + public double As({_unitEnumName} unit) + {{ + if(Unit == unit) + return Convert.ToDouble(Value); + + var converted = GetValueAs(unit); + return Convert.ToDouble(converted); + }} + + /// + public double As(UnitSystem unitSystem) + {{ + if(unitSystem == null) + throw new ArgumentNullException(nameof(unitSystem)); + + var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits); + + var firstUnitInfo = unitInfos.FirstOrDefault(); + if(firstUnitInfo == null) + throw new ArgumentException(""No units were found for the given UnitSystem."", nameof(unitSystem)); + + return As(firstUnitInfo.Value); + }} + + /// + double IQuantity.As(Enum unit) + {{ + if(!(unit is {_unitEnumName} unitAs{_unitEnumName})) + throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit)); + + return As(unitAs{_unitEnumName}); + }} + + /// + /// Converts this {_quantity.Name} to another {_quantity.Name} with the unit representation . + /// + /// A {_quantity.Name} with the specified unit. + public {_quantity.Name} ToUnit({_unitEnumName} unit) + {{ + var convertedValue = GetValueAs(unit); + return new {_quantity.Name}(convertedValue, unit); + }} + + /// + IQuantity IQuantity.ToUnit(Enum unit) + {{ + if(!(unit is {_unitEnumName} unitAs{_unitEnumName})) + throw new ArgumentException($""The given unit is of type {{unit.GetType()}}. Only {{typeof({_unitEnumName})}} is supported."", nameof(unit)); + + return ToUnit(unitAs{_unitEnumName}); + }} + + /// + public {_quantity.Name} ToUnit(UnitSystem unitSystem) + {{ + if(unitSystem == null) + throw new ArgumentNullException(nameof(unitSystem)); + + var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits); + + var firstUnitInfo = unitInfos.FirstOrDefault(); + if(firstUnitInfo == null) + throw new ArgumentException(""No units were found for the given UnitSystem."", nameof(unitSystem)); + + return ToUnit(firstUnitInfo.Value); + }} + + /// + IQuantity IQuantity.ToUnit(UnitSystem unitSystem) => ToUnit(unitSystem); + + /// + IQuantity<{_unitEnumName}> IQuantity<{_unitEnumName}>.ToUnit({_unitEnumName} unit) => ToUnit(unit); + + /// + IQuantity<{_unitEnumName}> IQuantity<{_unitEnumName}>.ToUnit(UnitSystem unitSystem) => ToUnit(unitSystem); + + /// + /// Converts the current value + unit to the base unit. + /// This is typically the first step in converting from one unit to another. + /// + /// The value in the base unit representation. + private {_valueType} GetValueInBaseUnit() + {{ + switch(Unit) + {{"); + foreach (var unit in _quantity.Units) + { + var func = unit.FromUnitToBaseFunc.Replace("x", "_value"); + Writer.WL($@" + case {_unitEnumName}.{unit.SingularName}: return {func};"); + } + + Writer.WL($@" + default: + throw new NotImplementedException($""Can not convert {{Unit}} to base units.""); + }} + }} + + private {_valueType} GetValueAs({_unitEnumName} unit) + {{ + if(Unit == unit) + return _value; + + var baseUnitValue = GetValueInBaseUnit(); + + switch(unit) + {{"); + foreach (var unit in _quantity.Units) + { + var func = unit.FromBaseToUnitFunc.Replace("x", "baseUnitValue"); + Writer.WL($@" + case {_unitEnumName}.{unit.SingularName}: return {func};"); + } + + Writer.WL(@" + default: + throw new NotImplementedException($""Can not convert {Unit} to {unit}.""); + } + } + + #endregion +"); + } + + private void GenerateToString() + { + Writer.WL($@" + #region ToString Methods + + /// + /// Gets the default string representation of value and unit. + /// + /// String representation. + public override string ToString() + {{ + return ToString(""g""); + }} + + /// + /// Gets the default string representation of value and unit using the given format provider. + /// + /// String representation. + /// Format to use for localization and number formatting. Defaults to if null. + public string ToString([CanBeNull] IFormatProvider provider) + {{ + return ToString(""g"", provider); + }} + + /// + /// Get string representation of value and unit. + /// + /// The number of significant digits after the radix point. + /// String representation. + /// Format to use for localization and number formatting. Defaults to if null. + [Obsolete(@""This method is deprecated and will be removed at a future release. Please use ToString(""""s2"""") or ToString(""""s2"""", provider) where 2 is an example of the number passed to significantDigitsAfterRadix."")] + public string ToString([CanBeNull] IFormatProvider provider, int significantDigitsAfterRadix) + {{ + var value = Convert.ToDouble(Value); + var format = UnitFormatter.GetFormat(value, significantDigitsAfterRadix); + return ToString(provider, format); + }} + + /// + /// Get string representation of value and unit. + /// + /// String format to use. Default: ""{{0:0.##}} {{1}} for value and unit abbreviation respectively."" + /// Arguments for string format. Value and unit are implictly included as arguments 0 and 1. + /// String representation. + /// Format to use for localization and number formatting. Defaults to if null. + [Obsolete(""This method is deprecated and will be removed at a future release. Please use string.Format()."")] + public string ToString([CanBeNull] IFormatProvider provider, [NotNull] string format, [NotNull] params object[] args) + {{ + if (format == null) throw new ArgumentNullException(nameof(format)); + if (args == null) throw new ArgumentNullException(nameof(args)); + + provider = provider ?? CultureInfo.CurrentUICulture; + + var value = Convert.ToDouble(Value); + var formatArgs = UnitFormatter.GetFormatArgs(Unit, value, provider, args); + return string.Format(provider, format, formatArgs); + }} + + /// + /// + /// Gets the string representation of this instance in the specified format string using . + /// + /// The format string. + /// The string representation. + public string ToString(string format) + {{ + return ToString(format, CultureInfo.CurrentUICulture); + }} + + /// + /// + /// Gets the string representation of this instance in the specified format string using the specified format provider, or if null. + /// + /// The format string. + /// Format to use for localization and number formatting. Defaults to if null. + /// The string representation. + public string ToString(string format, IFormatProvider formatProvider) + {{ + return QuantityFormatter.Format<{_unitEnumName}>(this, format, formatProvider); + }} + + #endregion +"); + } + + private void GenerateIConvertibleMethods() + { + Writer.WL($@" + #region IConvertible Methods + + TypeCode IConvertible.GetTypeCode() + {{ + return TypeCode.Object; + }} + + bool IConvertible.ToBoolean(IFormatProvider provider) + {{ + throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to bool is not supported.""); + }} + + byte IConvertible.ToByte(IFormatProvider provider) + {{ + return Convert.ToByte(_value); + }} + + char IConvertible.ToChar(IFormatProvider provider) + {{ + throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to char is not supported.""); + }} + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + {{ + throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to DateTime is not supported.""); + }} + + decimal IConvertible.ToDecimal(IFormatProvider provider) + {{ + return Convert.ToDecimal(_value); + }} + + double IConvertible.ToDouble(IFormatProvider provider) + {{ + return Convert.ToDouble(_value); + }} + + short IConvertible.ToInt16(IFormatProvider provider) + {{ + return Convert.ToInt16(_value); + }} + + int IConvertible.ToInt32(IFormatProvider provider) + {{ + return Convert.ToInt32(_value); + }} + + long IConvertible.ToInt64(IFormatProvider provider) + {{ + return Convert.ToInt64(_value); + }} + + sbyte IConvertible.ToSByte(IFormatProvider provider) + {{ + return Convert.ToSByte(_value); + }} + + float IConvertible.ToSingle(IFormatProvider provider) + {{ + return Convert.ToSingle(_value); + }} + + string IConvertible.ToString(IFormatProvider provider) + {{ + return ToString(""g"", provider); + }} + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + {{ + if(conversionType == typeof({_quantity.Name})) + return this; + else if(conversionType == typeof({_unitEnumName})) + return Unit; + else if(conversionType == typeof(QuantityType)) + return {_quantity.Name}.QuantityType; + else if(conversionType == typeof(BaseDimensions)) + return {_quantity.Name}.BaseDimensions; + else + throw new InvalidCastException($""Converting {{typeof({_quantity.Name})}} to {{conversionType}} is not supported.""); + }} + + ushort IConvertible.ToUInt16(IFormatProvider provider) + {{ + return Convert.ToUInt16(_value); + }} + + uint IConvertible.ToUInt32(IFormatProvider provider) + {{ + return Convert.ToUInt32(_value); + }} + + ulong IConvertible.ToUInt64(IFormatProvider provider) + {{ + return Convert.ToUInt64(_value); + }} + + #endregion"); + } + + /// + internal static string GetObsoleteAttributeOrNull(Quantity quantity) => GetObsoleteAttributeOrNull(quantity.ObsoleteText); + + /// + internal static string GetObsoleteAttributeOrNull(Unit unit) => GetObsoleteAttributeOrNull(unit.ObsoleteText); + + /// + /// Returns the Obsolete attribute if ObsoleteText has been defined on the JSON input - otherwise returns empty string + /// It is up to the consumer to wrap any padding/new lines in order to keep to correct indentation formats + /// + private static string GetObsoleteAttributeOrNull(string obsoleteText) => string.IsNullOrWhiteSpace(obsoleteText) + ? null + : $"[System.Obsolete({obsoleteText})]"; + } +} diff --git a/CodeGen/Generators/UnitsNetGen/QuantityTypeGenerator.cs b/CodeGen/Generators/UnitsNetGen/QuantityTypeGenerator.cs new file mode 100644 index 0000000000..b105c5cbc6 --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/QuantityTypeGenerator.cs @@ -0,0 +1,43 @@ +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class QuantityTypeGenerator : GeneratorBase + { + private readonly Quantity[] _quantities; + + public QuantityTypeGenerator(Quantity[] quantities) + { + _quantities = quantities; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +// ReSharper disable once CheckNamespace +namespace UnitsNet +{ + /// + /// Lists all generated quantities with the same name as the quantity struct type, + /// such as Length, Mass, Force etc. + /// This is useful for populating options in the UI, such as creating a generic conversion + /// tool with inputValue, quantityName, fromUnit and toUnit selectors. + /// + public enum QuantityType + { +// Missing XML comment for public type or member +#pragma warning disable CS1591 + Undefined = 0,"); + foreach (var quantity in _quantities) + Writer.WL($@" + {quantity.Name},"); + Writer.WL(@" +// Missing XML comment for public type or member +#pragma warning restore CS1591 + } +}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs b/CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs new file mode 100644 index 0000000000..25d9143e49 --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs @@ -0,0 +1,124 @@ +using CodeGen.Helpers; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class StaticQuantityGenerator : GeneratorBase + { + private readonly Quantity[] _quantities; + + public StaticQuantityGenerator(Quantity[] quantities) + { + _quantities = quantities; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +using System; +using System.Globalization; +using JetBrains.Annotations; +using UnitsNet.InternalHelpers; +using UnitsNet.Units; + +namespace UnitsNet +{ + /// + /// Dynamically parse or construct quantities when types are only known at runtime. + /// + public static partial class Quantity + { + /// + /// Dynamically constructs a quantity of the given with the value in the quantity's base units. + /// + /// The of the quantity to create. + /// The value to construct the quantity with. + /// The created quantity. + public static IQuantity FromQuantityType(QuantityType quantityType, QuantityValue value) + { + switch(quantityType) + {"); + foreach (var quantity in _quantities) + { + var quantityName = quantity.Name; + Writer.WL($@" + case QuantityType.{quantityName}: + return {quantityName}.From(value, {quantityName}.BaseUnit);"); + } + + Writer.WL(@" + default: + throw new ArgumentException($""{quantityType} is not a supported quantity type.""); + } + } + + /// + /// Try to dynamically construct a quantity. + /// + /// Numeric value. + /// Unit enum value. + /// The resulting quantity if successful, otherwise default. + /// True if successful with assigned the value, otherwise false. + public static bool TryFrom(QuantityValue value, Enum unit, out IQuantity quantity) + { + switch (unit) + {"); + foreach (var quantity in _quantities) + { + var quantityName = quantity.Name; + var unitTypeName = $"{quantityName}Unit"; + var unitValue = unitTypeName.ToCamelCase(); + Writer.WL($@" + case {unitTypeName} {unitValue}: + quantity = {quantityName}.From(value, {unitValue}); + return true;"); + } + + Writer.WL(@" + default: + { + quantity = default(IQuantity); + return false; + } + } + } + + /// + /// Try to dynamically parse a quantity string representation. + /// + /// The format provider to use for lookup. Defaults to if null. + /// Type of quantity, such as . + /// Quantity string representation, such as ""1.5 kg"". Must be compatible with given quantity type. + /// The resulting quantity if successful, otherwise default. + /// The parsed quantity. + public static bool TryParse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString, out IQuantity quantity) + { + quantity = default(IQuantity); + + if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType)) + return false; + + var parser = QuantityParser.Default; + + switch(quantityType) + {"); + foreach (var quantity in _quantities) + { + var quantityName = quantity.Name; + Writer.WL($@" + case Type _ when quantityType == typeof({quantityName}): + return parser.TryParse<{quantityName}, {quantityName}Unit>(quantityString, formatProvider, {quantityName}.From, out quantity);"); + } + + Writer.WL(@" + default: + return false; + } + } + } +}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetGen/UnitAbbreviationsCacheGenerator.cs b/CodeGen/Generators/UnitsNetGen/UnitAbbreviationsCacheGenerator.cs new file mode 100644 index 0000000000..26189a0172 --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/UnitAbbreviationsCacheGenerator.cs @@ -0,0 +1,58 @@ +using System.Linq; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class UnitAbbreviationsCacheGenerator : GeneratorBase + { + private readonly Quantity[] _quantities; + + public UnitAbbreviationsCacheGenerator(Quantity[] quantities) + { + _quantities = quantities; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +using System; +using UnitsNet.Units; + +// ReSharper disable RedundantCommaInArrayInitializer +// ReSharper disable once CheckNamespace +namespace UnitsNet +{ + public partial class UnitAbbreviationsCache + { + private static readonly (string CultureName, Type UnitType, int UnitValue, string[] UnitAbbreviations)[] GeneratedLocalizations + = new [] + {"); + foreach (var quantity in _quantities) + { + var unitEnumName = $"{quantity.Name}Unit"; + + foreach (var unit in quantity.Units) + { + foreach (var localization in unit.Localization) + { + var cultureName = localization.Culture; + + // All units must have a unit abbreviation, so fallback to "" for units with no abbreviations defined in JSON + var abbreviationParams = localization.Abbreviations.Any() + ? string.Join(", ", localization.Abbreviations.Select(abbr => $"\"{abbr}\"")) + : "\"\""; + Writer.WL($@" + (""{cultureName}"", typeof({unitEnumName}), (int){unitEnumName}.{unit.SingularName}, new string[]{{{abbreviationParams}}}),"); + } + } + } + + Writer.WL(@" + }; + } +}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetGen/UnitTestBaseClassGenerator.cs b/CodeGen/Generators/UnitsNetGen/UnitTestBaseClassGenerator.cs new file mode 100644 index 0000000000..41f6edb63f --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/UnitTestBaseClassGenerator.cs @@ -0,0 +1,311 @@ +using System; +using System.Linq; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class UnitTestBaseClassGenerator : GeneratorBase + { + private readonly Quantity _quantity; + private readonly Unit _baseUnit; + private readonly string _unitEnumName; + + public UnitTestBaseClassGenerator(Quantity quantity) + { + _quantity = quantity; + _baseUnit = quantity.Units.FirstOrDefault(u => u.SingularName == _quantity.BaseUnit) ?? + throw new ArgumentException($"No unit found with SingularName equal to BaseUnit [{_quantity.BaseUnit}]. This unit must be defined.", + nameof(quantity)); + _unitEnumName = $"{quantity.Name}Unit"; + } + + public override string Generate() + { + var baseUnitVariableName = _baseUnit.SingularName.ToLowerInvariant(); + + Writer.WL(GeneratedFileHeader); + Writer.WL($@" +using System; +using System.Linq; +using UnitsNet.Units; +using Xunit; + +// Disable build warning CS1718: Comparison made to same variable; did you mean to compare something else? +#pragma warning disable 1718 + +// ReSharper disable once CheckNamespace +namespace UnitsNet.Tests +{{ + /// + /// Test of {_quantity.Name}. + /// +// ReSharper disable once PartialTypeWithSinglePart + public abstract partial class {_quantity.Name}TestsBase + {{"); + foreach (var unit in _quantity.Units) Writer.WL($@" + protected abstract double {unit.PluralName}InOne{_baseUnit.SingularName} {{ get; }}"); + + Writer.WL(""); + Writer.WL($@" +// ReSharper disable VirtualMemberNeverOverriden.Global"); + foreach (var unit in _quantity.Units) Writer.WL($@" + protected virtual double {unit.PluralName}Tolerance {{ get {{ return 1e-5; }} }}"); Writer.WL($@" +// ReSharper restore VirtualMemberNeverOverriden.Global + + [Fact] + public void Ctor_WithUndefinedUnit_ThrowsArgumentException() + {{ + Assert.Throws(() => new {_quantity.Name}(({_quantity.BaseType})0.0, {_unitEnumName}.Undefined)); + }} +"); + if (_quantity.BaseType == "double") Writer.WL($@" + [Fact] + public void Ctor_WithInfinityValue_ThrowsArgumentException() + {{ + Assert.Throws(() => new {_quantity.Name}(double.PositiveInfinity, {_unitEnumName}.{_baseUnit.SingularName})); + Assert.Throws(() => new {_quantity.Name}(double.NegativeInfinity, {_unitEnumName}.{_baseUnit.SingularName})); + }} + + [Fact] + public void Ctor_WithNaNValue_ThrowsArgumentException() + {{ + Assert.Throws(() => new {_quantity.Name}(double.NaN, {_unitEnumName}.{_baseUnit.SingularName})); + }} +"); Writer.WL($@" + + [Fact] + public void {_baseUnit.SingularName}To{_quantity.Name}Units() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1);"); + + foreach (var unit in _quantity.Units) Writer.WL($@" + AssertEx.EqualTolerance({unit.PluralName}InOne{_baseUnit.SingularName}, {baseUnitVariableName}.{unit.PluralName}, {unit.PluralName}Tolerance);"); + Writer.WL($@" + }} + + [Fact] + public void FromValueAndUnit() + {{"); + foreach (var unit in _quantity.Units) Writer.WL($@" + AssertEx.EqualTolerance(1, {_quantity.Name}.From(1, {_unitEnumName}.{unit.SingularName}).{unit.PluralName}, {unit.PluralName}Tolerance);"); + Writer.WL($@" + }} +"); + if (_quantity.BaseType == "double") Writer.WL($@" + [Fact] + public void From{_baseUnit.PluralName}_WithInfinityValue_ThrowsArgumentException() + {{ + Assert.Throws(() => {_quantity.Name}.From{_baseUnit.PluralName}(double.PositiveInfinity)); + Assert.Throws(() => {_quantity.Name}.From{_baseUnit.PluralName}(double.NegativeInfinity)); + }} + + [Fact] + public void From{_baseUnit.PluralName}_WithNanValue_ThrowsArgumentException() + {{ + Assert.Throws(() => {_quantity.Name}.From{_baseUnit.PluralName}(double.NaN)); + }} +"); Writer.WL($@" + + [Fact] + public void As() + {{ + var {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1);"); + foreach (var unit in _quantity.Units) Writer.WL($@" + AssertEx.EqualTolerance({unit.PluralName}InOne{_baseUnit.SingularName}, {baseUnitVariableName}.As({_unitEnumName}.{unit.SingularName}), {unit.PluralName}Tolerance);"); + Writer.WL($@" + }} + + [Fact] + public void ToUnit() + {{ + var {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1);"); + foreach (var unit in _quantity.Units) + { + var asQuantityVariableName = $"{unit.SingularName.ToLowerInvariant()}Quantity"; + + Writer.WL(""); + Writer.WL($@" + var {asQuantityVariableName} = {baseUnitVariableName}.ToUnit({_unitEnumName}.{unit.SingularName}); + AssertEx.EqualTolerance({unit.PluralName}InOne{_baseUnit.SingularName}, (double){asQuantityVariableName}.Value, {unit.PluralName}Tolerance); + Assert.Equal({_unitEnumName}.{unit.SingularName}, {asQuantityVariableName}.Unit);"); + } + Writer.WL($@" + }} + + [Fact] + public void ConversionRoundTrip() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1);"); + foreach (var unit in _quantity.Units) Writer.WL($@" + AssertEx.EqualTolerance(1, {_quantity.Name}.From{unit.PluralName}({baseUnitVariableName}.{unit.PluralName}).{_baseUnit.PluralName}, {unit.PluralName}Tolerance);"); + Writer.WL($@" + }} +"); + if (_quantity.Logarithmic) + { + var unit = _quantity.Units.Last(); + Writer.WL($@" + [Fact] + public void LogarithmicArithmeticOperators() + {{ + {_quantity.Name} v = {_quantity.Name}.From{_baseUnit.PluralName}(40); + AssertEx.EqualTolerance(-40, -v.{_baseUnit.PluralName}, {unit.PluralName}Tolerance); + AssertLogarithmicAddition(); + AssertLogarithmicSubtraction(); + AssertEx.EqualTolerance(50, (v*10).{_baseUnit.PluralName}, {unit.PluralName}Tolerance); + AssertEx.EqualTolerance(50, (10*v).{_baseUnit.PluralName}, {unit.PluralName}Tolerance); + AssertEx.EqualTolerance(35, (v/5).{_baseUnit.PluralName}, {unit.PluralName}Tolerance); + AssertEx.EqualTolerance(35, v/{_quantity.Name}.From{_baseUnit.PluralName}(5), {unit.PluralName}Tolerance); + }} + + protected abstract void AssertLogarithmicAddition(); + + protected abstract void AssertLogarithmicSubtraction(); +"); + } + else if (_quantity.GenerateArithmetic) + { + Writer.WL($@" + [Fact] + public void ArithmeticOperators() + {{ + {_quantity.Name} v = {_quantity.Name}.From{_baseUnit.PluralName}(1); + AssertEx.EqualTolerance(-1, -v.{_baseUnit.PluralName}, {_baseUnit.PluralName}Tolerance); + AssertEx.EqualTolerance(2, ({_quantity.Name}.From{_baseUnit.PluralName}(3)-v).{_baseUnit.PluralName}, {_baseUnit.PluralName}Tolerance); + AssertEx.EqualTolerance(2, (v + v).{_baseUnit.PluralName}, {_baseUnit.PluralName}Tolerance); + AssertEx.EqualTolerance(10, (v*10).{_baseUnit.PluralName}, {_baseUnit.PluralName}Tolerance); + AssertEx.EqualTolerance(10, (10*v).{_baseUnit.PluralName}, {_baseUnit.PluralName}Tolerance); + AssertEx.EqualTolerance(2, ({_quantity.Name}.From{_baseUnit.PluralName}(10)/5).{_baseUnit.PluralName}, {_baseUnit.PluralName}Tolerance); + AssertEx.EqualTolerance(2, {_quantity.Name}.From{_baseUnit.PluralName}(10)/{_quantity.Name}.From{_baseUnit.PluralName}(5), {_baseUnit.PluralName}Tolerance); + }} +"); + } + else + { + Writer.WL(""); + } + + Writer.WL($@" + [Fact] + public void ComparisonOperators() + {{ + {_quantity.Name} one{_baseUnit.SingularName} = {_quantity.Name}.From{_baseUnit.PluralName}(1); + {_quantity.Name} two{_baseUnit.PluralName} = {_quantity.Name}.From{_baseUnit.PluralName}(2); + + Assert.True(one{_baseUnit.SingularName} < two{_baseUnit.PluralName}); + Assert.True(one{_baseUnit.SingularName} <= two{_baseUnit.PluralName}); + Assert.True(two{_baseUnit.PluralName} > one{_baseUnit.SingularName}); + Assert.True(two{_baseUnit.PluralName} >= one{_baseUnit.SingularName}); + + Assert.False(one{_baseUnit.SingularName} > two{_baseUnit.PluralName}); + Assert.False(one{_baseUnit.SingularName} >= two{_baseUnit.PluralName}); + Assert.False(two{_baseUnit.PluralName} < one{_baseUnit.SingularName}); + Assert.False(two{_baseUnit.PluralName} <= one{_baseUnit.SingularName}); + }} + + [Fact] + public void CompareToIsImplemented() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1); + Assert.Equal(0, {baseUnitVariableName}.CompareTo({baseUnitVariableName})); + Assert.True({baseUnitVariableName}.CompareTo({_quantity.Name}.Zero) > 0); + Assert.True({_quantity.Name}.Zero.CompareTo({baseUnitVariableName}) < 0); + }} + + [Fact] + public void CompareToThrowsOnTypeMismatch() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1); + Assert.Throws(() => {baseUnitVariableName}.CompareTo(new object())); + }} + + [Fact] + public void CompareToThrowsOnNull() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1); + Assert.Throws(() => {baseUnitVariableName}.CompareTo(null)); + }} + + [Fact] + public void EqualityOperators() + {{ + var a = {_quantity.Name}.From{_baseUnit.PluralName}(1); + var b = {_quantity.Name}.From{_baseUnit.PluralName}(2); + + // ReSharper disable EqualExpressionComparison + + Assert.True(a == a); + Assert.False(a != a); + + Assert.True(a != b); + Assert.False(a == b); + + Assert.False(a == null); + Assert.False(null == a); + +// ReSharper restore EqualExpressionComparison + }} + + [Fact] + public void EqualsIsImplemented() + {{ + var a = {_quantity.Name}.From{_baseUnit.PluralName}(1); + var b = {_quantity.Name}.From{_baseUnit.PluralName}(2); + + Assert.True(a.Equals(a)); + Assert.False(a.Equals(b)); + Assert.False(a.Equals(null)); + }} + + [Fact] + public void EqualsRelativeToleranceIsImplemented() + {{ + var v = {_quantity.Name}.From{_baseUnit.PluralName}(1); + Assert.True(v.Equals({_quantity.Name}.From{_baseUnit.PluralName}(1), {_baseUnit.PluralName}Tolerance, ComparisonType.Relative)); + Assert.False(v.Equals({_quantity.Name}.Zero, {_baseUnit.PluralName}Tolerance, ComparisonType.Relative)); + }} + + [Fact] + public void EqualsReturnsFalseOnTypeMismatch() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1); + Assert.False({baseUnitVariableName}.Equals(new object())); + }} + + [Fact] + public void EqualsReturnsFalseOnNull() + {{ + {_quantity.Name} {baseUnitVariableName} = {_quantity.Name}.From{_baseUnit.PluralName}(1); + Assert.False({baseUnitVariableName}.Equals(null)); + }} + + [Fact] + public void UnitsDoesNotContainUndefined() + {{ + Assert.DoesNotContain({_unitEnumName}.Undefined, {_quantity.Name}.Units); + }} + + [Fact] + public void HasAtLeastOneAbbreviationSpecified() + {{ + var units = Enum.GetValues(typeof({_unitEnumName})).Cast<{_unitEnumName}>(); + foreach(var unit in units) + {{ + if(unit == {_unitEnumName}.Undefined) + continue; + + var defaultAbbreviation = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit); + }} + }} + + [Fact] + public void BaseDimensionsShouldNeverBeNull() + {{ + Assert.False({_quantity.Name}.BaseDimensions is null); + }} + }} +}}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetGen/UnitTestStubGenerator.cs b/CodeGen/Generators/UnitsNetGen/UnitTestStubGenerator.cs new file mode 100644 index 0000000000..3580502e0b --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/UnitTestStubGenerator.cs @@ -0,0 +1,30 @@ +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class UnitTestStubGenerator : GeneratorBase + { + private readonly Quantity _quantity; + + public UnitTestStubGenerator(Quantity quantity) + { + _quantity = quantity; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL($@" +using System; + +namespace UnitsNet.Tests.CustomCode +{{ + public class {_quantity.Name}Tests : {_quantity.Name}TestsBase + {{ + // Override properties in base class here + }} +}}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetGen/UnitTypeGenerator.cs b/CodeGen/Generators/UnitsNetGen/UnitTypeGenerator.cs new file mode 100644 index 0000000000..eaf6220511 --- /dev/null +++ b/CodeGen/Generators/UnitsNetGen/UnitTypeGenerator.cs @@ -0,0 +1,61 @@ +using CodeGen.Helpers; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetGen +{ + internal class UnitTypeGenerator : GeneratorBase + { + private readonly Quantity _quantity; + private readonly string _unitEnumName; + + public UnitTypeGenerator(Quantity quantity) + { + _quantity = quantity; + _unitEnumName = $"{quantity.Name}Unit"; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL($@" +// ReSharper disable once CheckNamespace +namespace UnitsNet.Units +{{ + // Disable missing XML comment warnings for the generated unit enums. + #pragma warning disable 1591 + + public enum {_unitEnumName} + {{ + Undefined = 0,"); + + foreach (var unit in _quantity.Units) + { + if (unit.XmlDocSummary.HasText()) + { + Writer.WL(); + Writer.WL($@" + /// + /// {unit.XmlDocSummary} + /// "); + } + + if (unit.XmlDocRemarks.HasText()) + { + Writer.WL($@" + /// {unit.XmlDocRemarks}"); + } + + Writer.WLIfText(2, QuantityGenerator.GetObsoleteAttributeOrNull(unit)); + Writer.WL($@" + {unit.SingularName},"); + } + + Writer.WL($@" + }} + + #pragma warning restore 1591 +}}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetGenerator.cs b/CodeGen/Generators/UnitsNetGenerator.cs new file mode 100644 index 0000000000..0155b07462 --- /dev/null +++ b/CodeGen/Generators/UnitsNetGenerator.cs @@ -0,0 +1,267 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using CodeGen.Generators.UnitsNetGen; +using CodeGen.Helpers; +using CodeGen.JsonTypes; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serilog; + +namespace CodeGen.Generators +{ + internal static class UnitsNetGenerator + { + private const int AlignPad = 35; + + private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings + { + // Don't override the C# default assigned values if no value is set in JSON + NullValueHandling = NullValueHandling.Ignore + }; + + public static void Generate(DirectoryInfo repositoryRoot) + { + if (repositoryRoot == null) throw new ArgumentNullException(nameof(repositoryRoot)); + var root = repositoryRoot.FullName; + + var templatesDir = Path.Combine(root, "Common/UnitDefinitions"); + var jsonFiles = Directory.GetFiles(templatesDir, "*.json"); + var quantities = jsonFiles.Select(ParseQuantityFile).ToArray(); + + foreach (Quantity quantity in quantities) + { + var sb = new StringBuilder($"{quantity.Name}:".PadRight(AlignPad)); + GenerateQuantity(sb, quantity, $"{root}/UnitsNet/GeneratedCode/Quantities/{quantity.Name}.NetFramework.g.cs"); // TODO Remove NetFramework suffix + GenerateUnitType(sb, quantity, $"{root}/UnitsNet/GeneratedCode/Units/{quantity.Name}Unit.g.cs"); + GenerateUnitTestBaseClass(sb, quantity, $"{root}/UnitsNet.Tests/GeneratedCode/{quantity.Name}TestsBase.g.cs"); + GenerateUnitTestClassIfNotExists(sb, quantity, $"{root}/UnitsNet.Tests/CustomCode/{quantity.Name}Tests.cs"); + Log.Information(sb.ToString()); + } + + Log.Information(""); + GenerateUnitAbbreviationsCache(quantities, $"{root}/UnitsNet/GeneratedCode/UnitAbbreviationsCache.g.cs"); + GenerateQuantityType(quantities, $"{root}/UnitsNet/GeneratedCode/QuantityType.g.cs"); + GenerateStaticQuantity(quantities, $"{root}/UnitsNet/GeneratedCode/Quantity.g.cs"); + + var unitCount = quantities.SelectMany(q => q.Units).Count(); + Log.Information(""); + Log.Information($"Total of {unitCount} units and {quantities.Length} quantities."); + Log.Information(""); + } + + private static Quantity ParseQuantityFile(string jsonFile) + { + var quantity = JsonConvert.DeserializeObject(File.ReadAllText(jsonFile, Encoding.UTF8), JsonSerializerSettings); + AddPrefixUnits(quantity); + FixConversionFunctionsForDecimalValueTypes(quantity); + OrderUnitsByName(quantity); + return quantity; + } + + private static void GenerateUnitTestClassIfNotExists(StringBuilder sb, Quantity quantity, string filePath) + { + if (File.Exists(filePath)) + { + sb.Append("test stub(skip) "); + return; + } + + string content = new UnitTestStubGenerator(quantity).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + sb.Append("test stub(OK) "); + } + + private static void GenerateQuantity(StringBuilder sb, Quantity quantity, string filePath) + { + string content = new QuantityGenerator(quantity).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + sb.Append("quantity(OK) "); + } + + private static void GenerateUnitType(StringBuilder sb, Quantity quantity, string filePath) + { + string content = new UnitTypeGenerator(quantity).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + sb.Append("unit(OK) "); + } + + private static void GenerateUnitTestBaseClass(StringBuilder sb, Quantity quantity, string filePath) + { + string content = new UnitTestBaseClassGenerator(quantity).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + sb.Append("test base(OK) "); + } + + private static void GenerateUnitAbbreviationsCache(Quantity[] quantities, string filePath) + { + string content = new UnitAbbreviationsCacheGenerator(quantities).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + Log.Information("UnitAbbreviationsCache.g.cs: ".PadRight(AlignPad) + "(OK)"); + } + + private static void GenerateQuantityType(Quantity[] quantities, string filePath) + { + string content = new QuantityTypeGenerator(quantities).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + Log.Information("QuantityType.g.cs: ".PadRight(AlignPad) + "(OK)"); + } + + private static void GenerateStaticQuantity(Quantity[] quantities, string filePath) + { + string content = new StaticQuantityGenerator(quantities).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + Log.Information("Quantity.g.cs: ".PadRight(AlignPad) + "(OK)"); + } + + private static void OrderUnitsByName(Quantity quantity) + { + quantity.Units = quantity.Units.OrderBy(u => u.SingularName).ToArray(); + } + + private static void FixConversionFunctionsForDecimalValueTypes(Quantity quantity) + { + foreach (Unit u in quantity.Units) + { + // Use decimal for internal calculations if base type is not double, such as for long or int. + if (string.Equals(quantity.BaseType, "decimal", StringComparison.OrdinalIgnoreCase)) + { + // Change any double literals like "1024d" to decimal literals "1024m" + u.FromUnitToBaseFunc = u.FromUnitToBaseFunc.Replace("d", "m"); + u.FromBaseToUnitFunc = u.FromBaseToUnitFunc.Replace("d", "m"); + } + } + } + + private static void AddPrefixUnits(Quantity quantity) + { + var unitsToAdd = new List(); + foreach (Unit unit in quantity.Units) + { + // "Kilo", "Nano" etc. + for (var prefixIndex = 0; prefixIndex < unit.Prefixes.Length; prefixIndex++) + { + Prefix prefix = unit.Prefixes[prefixIndex]; + PrefixInfo prefixInfo = PrefixInfo.Entries[prefix]; + + unitsToAdd.Add(new Unit + { + SingularName = $"{prefix}{unit.SingularName.ToCamelCase()}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter" + PluralName = $"{prefix}{unit.PluralName.ToCamelCase()}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter" + BaseUnits = null, // Can we determine this somehow? + FromBaseToUnitFunc = $"({unit.FromBaseToUnitFunc}) / {prefixInfo.Factor}", + FromUnitToBaseFunc = $"({unit.FromUnitToBaseFunc}) * {prefixInfo.Factor}", + Localization = GetLocalizationForPrefixUnit(unit, prefixIndex, prefixInfo, quantity.Name), + }); + } + } + + quantity.Units = quantity.Units.Concat(unitsToAdd).ToArray(); + } + + private static Localization[] GetLocalizationForPrefixUnit(Unit unit, int prefixIndex, PrefixInfo prefixInfo, string quantityName) + { + string[] GetUnitAbbreviationsForPrefix(Localization loc) + { + // If no custom abbreviations are specified, prepend the default prefix to each unit abbreviation: kilo ("k") + meter ("m") => kilometer ("km") + if (loc.AbbreviationsWithPrefixes == null || !loc.AbbreviationsWithPrefixes.Any()) + { + string prefix = prefixInfo.Abbreviation; + return loc.Abbreviations.Select(unitAbbreviation => $"{prefix}{unitAbbreviation}").ToArray(); + } + + /* + Prepend prefix to all abbreviations of a unit. + Some languages, like Russian, you can't simply prepend "k" for kilo prefix, so the prefix abbreviations must be explicitly defined + with AbbreviationsWithPrefixes. + + Example 1 - Torque.Newtonmeter has only a single abbreviation in Russian, so AbbreviationsWithPrefixes is an array of strings mapped to each prefix + + { + "SingularName": "NewtonMeter", + "PluralName": "NewtonMeters", + "FromUnitToBaseFunc": "x", + "FromBaseToUnitFunc": "x", + "Prefixes": [ "Kilo", "Mega" ], + "Localization": [ + { + "Culture": "en-US", + "Abbreviations": [ "N·m" ] + }, + { + "Culture": "ru-RU", + "Abbreviations": [ "Н·м" ], + "AbbreviationsWithPrefixes": [ "кН·м", "МН·м" ] + } + ] + }, + + Example 2 - Duration.Second has 3 prefixes and 2 abbreviations in Russian, so AbbreviationsWithPrefixes is an array of 3 items where each + represents the unit abbreviations for that prefix - typically a variant of those in "Abbreviations", but the counts don't have to match. + + { + "SingularName": "Second", + "PluralName": "Seconds", + "BaseUnits": { + "T": "Second" + }, + "FromUnitToBaseFunc": "x", + "FromBaseToUnitFunc": "x", + "Prefixes": [ "Nano", "Micro", "Milli" ], + "Localization": [ + { + "Culture": "en-US", + "Abbreviations": [ "s", "sec", "secs", "second", "seconds" ] + }, + { + "Culture": "ru-RU", + "Abbreviations": [ "с", "сек" ], + "AbbreviationsWithPrefixes": [ ["нс", "нсек"], ["мкс", "мксек"], ["мс", "мсек"] ] + } + ] + } + */ + + EnsureValidAbbreviationsWithPrefixes(loc, unit, quantityName); + JToken abbreviationsForPrefix = loc.AbbreviationsWithPrefixes[prefixIndex]; + switch (abbreviationsForPrefix.Type) + { + case JTokenType.Array: + return abbreviationsForPrefix.ToObject(); + case JTokenType.String: + return new[] {abbreviationsForPrefix.ToObject()}; + default: + throw new NotSupportedException("Expect AbbreviationsWithPrefixes to be an array of strings or string arrays."); + } + } + + Localization WithPrefixes(Localization loc) + { + string[] unitAbbreviationsForPrefix = GetUnitAbbreviationsForPrefix(loc); + + return new Localization + { + Culture = loc.Culture, + Abbreviations = unitAbbreviationsForPrefix, + }; + } + + return unit.Localization.Select(WithPrefixes).ToArray(); + } + + private static void EnsureValidAbbreviationsWithPrefixes(Localization localization, Unit unit, string quantityName) + { + if (localization.AbbreviationsWithPrefixes.Length > 0 && + localization.AbbreviationsWithPrefixes.Length != unit.Prefixes.Length) + { + throw new InvalidDataException( + $"The Prefixes array length {unit.Prefixes.Length} does not match Localization.AbbreviationsWithPrefixes array length {localization.AbbreviationsWithPrefixes.Length} for {quantityName}.{unit.SingularName}"); + } + } + } +} diff --git a/CodeGen/Generators/UnitsNetWrcGen/QuantityGenerator.cs b/CodeGen/Generators/UnitsNetWrcGen/QuantityGenerator.cs new file mode 100644 index 0000000000..96067ffdcc --- /dev/null +++ b/CodeGen/Generators/UnitsNetWrcGen/QuantityGenerator.cs @@ -0,0 +1,750 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using CodeGen.Helpers; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetWrcGen +{ + internal class QuantityGenerator : GeneratorBase + { + private readonly Quantity _quantity; + + private readonly bool _isDimensionless; + private readonly string _unitEnumName; + private readonly string _valueType; + private readonly Unit _baseUnit; + + public QuantityGenerator(Quantity quantity) + { + _quantity = quantity ?? throw new ArgumentNullException(nameof(quantity)); + + _baseUnit = quantity.Units.FirstOrDefault(u => u.SingularName == _quantity.BaseUnit) ?? + throw new ArgumentException($"No unit found with SingularName equal to BaseUnit [{_quantity.BaseUnit}]. This unit must be defined.", + nameof(quantity)); + + _valueType = quantity.BaseType; + _unitEnumName = $"{quantity.Name}Unit"; + + BaseDimensions baseDimensions = quantity.BaseDimensions; + _isDimensionless = baseDimensions == null || + baseDimensions.L == 0 && + baseDimensions.M == 0 && + baseDimensions.T == 0 && + baseDimensions.I == 0 && + baseDimensions.Θ == 0 && + baseDimensions.N == 0 && + baseDimensions.J == 0; + + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +using System; +using System.Globalization; +using System.Linq; +using JetBrains.Annotations; +using UnitsNet.Units; +using UnitsNet.InternalHelpers; + +// ReSharper disable once CheckNamespace + +namespace UnitsNet +{"); + Writer.WLIfText(1, GetObsoleteAttributeOrNull(_quantity)); + Writer.WL($@" + /// + /// {_quantity.XmlDoc} + /// "); + + Writer.WLCondition(_quantity.XmlDocRemarks.HasText(), $@" + /// + /// {_quantity.XmlDocRemarks} + /// "); + + Writer.WL($@" + // Windows Runtime Component has constraints on public types: https://msdn.microsoft.com/en-us/library/br230301.aspx#Declaring types in Windows Runtime Components + // Public structures can't have any members other than public fields, and those fields must be value types or strings. + // Public classes must be sealed (NotInheritable in Visual Basic). If your programming model requires polymorphism, you can create a public interface and implement that interface on the classes that must be polymorphic. + public sealed partial class {_quantity.Name} : IQuantity + {{ + /// + /// The numeric value this quantity was constructed with. + /// + private readonly {_quantity.BaseType} _value; + + /// + /// The unit this quantity was constructed with. + /// + private readonly {_unitEnumName}? _unit; +"); + GenerateStaticConstructor(); + GenerateInstanceConstructors(); + GenerateStaticProperties(); + GenerateProperties(); + GenerateConversionProperties(); + GenerateStaticMethods(); + GenerateStaticFactoryMethods(); + GenerateStaticParseMethods(); + GenerateEqualityAndComparison(); + GenerateConversionMethods(); + GenerateToString(); + + Writer.WL($@" + private static IFormatProvider GetFormatProviderFromCultureName([CanBeNull] string cultureName) + {{ + return cultureName != null ? new CultureInfo(cultureName) : (IFormatProvider)null; + }} + }} +}}"); + return Writer.ToString(); + } + + private void GenerateStaticConstructor() + { + var baseDimensions = _quantity.BaseDimensions; + Writer.WL($@" + static {_quantity.Name}() + {{"); + Writer.WL(_isDimensionless + ? @" + BaseDimensions = BaseDimensions.Dimensionless;" + : $@" + BaseDimensions = new BaseDimensions({baseDimensions.L}, {baseDimensions.M}, {baseDimensions.T}, {baseDimensions.I}, {baseDimensions.Θ}, {baseDimensions.N}, {baseDimensions.J});"); + + Writer.WL($@" + Info = new QuantityInfo(QuantityType.{_quantity.Name}, Units.Cast().ToArray(), BaseUnit, Zero, BaseDimensions); + }} +"); + } + + private void GenerateInstanceConstructors() + { + Writer.WL($@" + /// + /// Creates the quantity with a value of 0 in the base unit {_baseUnit.SingularName}. + /// + /// + /// Windows Runtime Component requires a default constructor. + /// + public {_quantity.Name}() + {{ + _value = 0; + _unit = BaseUnit; + }} + + /// + /// Creates the quantity with the given numeric value and unit. + /// + /// The numeric value to contruct this quantity with. + /// The unit representation to contruct this quantity with. + /// Value parameter cannot be named 'value' due to constraint when targeting Windows Runtime Component. + /// If value is NaN or Infinity. + private {_quantity.Name}({_quantity.BaseType} numericValue, {_unitEnumName} unit) + {{ + if(unit == {_unitEnumName}.Undefined) + throw new ArgumentException(""The quantity can not be created with an undefined unit."", nameof(unit)); +"); + + Writer.WL(_quantity.BaseType == "double" + ? @" + _value = Guard.EnsureValidNumber(numericValue, nameof(numericValue));" + : @" + _value = numericValue;"); + Writer.WL($@" + _unit = unit; + }} +"); + } + + private void GenerateStaticProperties() + { + Writer.WL($@" + #region Static Properties + + /// + /// Information about the quantity type, such as unit values and names. + /// + internal static QuantityInfo Info {{ get; }} + + /// + /// The of this quantity. + /// + public static BaseDimensions BaseDimensions {{ get; }} + + /// + /// The base unit of {_quantity.Name}, which is {_quantity.BaseUnit}. All conversions go via this value. + /// + public static {_unitEnumName} BaseUnit {{ get; }} = {_unitEnumName}.{_quantity.BaseUnit}; + + /// + /// Represents the largest possible value of {_quantity.Name} + /// + public static {_quantity.Name} MaxValue {{ get; }} = new {_quantity.Name}({_valueType}.MaxValue, BaseUnit); + + /// + /// Represents the smallest possible value of {_quantity.Name} + /// + public static {_quantity.Name} MinValue {{ get; }} = new {_quantity.Name}({_valueType}.MinValue, BaseUnit); + + /// + /// The of this quantity. + /// + public static QuantityType QuantityType {{ get; }} = QuantityType.{_quantity.Name}; + + /// + /// All units of measurement for the {_quantity.Name} quantity. + /// + public static {_unitEnumName}[] Units {{ get; }} = Enum.GetValues(typeof({_unitEnumName})).Cast<{_unitEnumName}>().Except(new {_unitEnumName}[]{{ {_unitEnumName}.Undefined }}).ToArray(); + + /// + /// Gets an instance of this quantity with a value of 0 in the base unit {_quantity.BaseUnit}. + /// + public static {_quantity.Name} Zero {{ get; }} = new {_quantity.Name}(0, BaseUnit); + + #endregion +"); + } + + private void GenerateProperties() + { + Writer.WL($@" + #region Properties + + /// + /// The numeric value this quantity was constructed with. + /// + public double Value => Convert.ToDouble(_value); +"); + + Writer.WL($@" + /// + object IQuantity.Unit => Unit; + + /// + /// The unit this quantity was constructed with -or- if default ctor was used. + /// + public {_unitEnumName} Unit => _unit.GetValueOrDefault(BaseUnit); + + internal QuantityInfo QuantityInfo => Info; + + /// + /// The of this quantity. + /// + public QuantityType Type => {_quantity.Name}.QuantityType; + + /// + /// The of this quantity. + /// + public BaseDimensions Dimensions => {_quantity.Name}.BaseDimensions; + + #endregion +"); + } + + private void GenerateConversionProperties() + { + Writer.WL(@" + #region Conversion Properties +"); + foreach (var unit in _quantity.Units) + { + Writer.WL($@" + /// + /// Get {_quantity.Name} in {unit.PluralName}. + /// "); + Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit)); + Writer.WL($@" + public double {unit.PluralName} => As({_unitEnumName}.{unit.SingularName}); +"); + } + + Writer.WL(@" + + #endregion +"); + } + + private void GenerateStaticMethods() + { + Writer.WL($@" + + #region Static Methods + + /// + /// Get unit abbreviation string. + /// + /// Unit to get abbreviation for. + /// Unit abbreviation string. + public static string GetAbbreviation({_unitEnumName} unit) + {{ + return GetAbbreviation(unit, null); + }} + + /// + /// Get unit abbreviation string. + /// + /// Unit to get abbreviation for. + /// Unit abbreviation string. + /// Name of culture (ex: ""en-US"") to use when parsing number and unit. Defaults to if null. + public static string GetAbbreviation({_quantity.Name}Unit unit, [CanBeNull] string cultureName) + {{ + IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); + return UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit, provider); + }} + + #endregion +"); + } + + private void GenerateStaticFactoryMethods() + { + Writer.WL(@" + #region Static Factory Methods +"); + foreach (var unit in _quantity.Units) + { + var valueParamName = unit.PluralName.ToLowerInvariant(); + Writer.WL($@" + /// + /// Get {_quantity.Name} from {unit.PluralName}. + /// + /// If value is NaN or Infinity."); + Writer.WLIfText(2, GetObsoleteAttributeOrNull(unit)); + Writer.WL($@" + [Windows.Foundation.Metadata.DefaultOverload] + public static {_quantity.Name} From{unit.PluralName}(double {valueParamName}) + {{ + {_valueType} value = ({_valueType}) {valueParamName}; + return new {_quantity.Name}(value, {_unitEnumName}.{unit.SingularName}); + }}"); + } + + Writer.WL(); + Writer.WL($@" + /// + /// Dynamically convert from value and unit enum to . + /// + /// Value to convert from. + /// Unit to convert from. + /// {_quantity.Name} unit value. + // Fix name conflict with parameter ""value"" + [return: System.Runtime.InteropServices.WindowsRuntime.ReturnValueName(""returnValue"")] + public static {_quantity.Name} From(double value, {_unitEnumName} fromUnit) + {{ + return new {_quantity.Name}(({_valueType})value, fromUnit); + }} + + #endregion +"); + } + + private void GenerateStaticParseMethods() + { + Writer.WL($@" + #region Static Parse Methods + + /// + /// Parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// + /// Expected string to have one or two pairs of quantity and unit in the format + /// ""<quantity> <unit>"". Eg. ""5.5 m"" or ""1ft 2in"" + /// + /// + /// More than one unit is represented by the specified unit abbreviation. + /// Example: Volume.Parse(""1 cup"") will throw, because it can refer to any of + /// , and . + /// + /// + /// If anything else goes wrong, typically due to a bug or unhandled case. + /// We wrap exceptions in to allow you to distinguish + /// Units.NET exceptions from other exceptions. + /// + public static {_quantity.Name} Parse(string str) + {{ + return Parse(str, null); + }} + + /// + /// Parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// + /// Expected string to have one or two pairs of quantity and unit in the format + /// ""<quantity> <unit>"". Eg. ""5.5 m"" or ""1ft 2in"" + /// + /// + /// More than one unit is represented by the specified unit abbreviation. + /// Example: Volume.Parse(""1 cup"") will throw, because it can refer to any of + /// , and . + /// + /// + /// If anything else goes wrong, typically due to a bug or unhandled case. + /// We wrap exceptions in to allow you to distinguish + /// Units.NET exceptions from other exceptions. + /// + /// Name of culture (ex: ""en-US"") to use when parsing number and unit. Defaults to if null. + public static {_quantity.Name} Parse(string str, [CanBeNull] string cultureName) + {{ + IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); + return QuantityParser.Default.Parse<{_quantity.Name}, {_unitEnumName}>( + str, + provider, + From); + }} + + /// + /// Try to parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// Resulting unit quantity if successful. + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + public static bool TryParse([CanBeNull] string str, out {_quantity.Name} result) + {{ + return TryParse(str, null, out result); + }} + + /// + /// Try to parse a string with one or two quantities of the format ""<quantity> <unit>"". + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// Resulting unit quantity if successful. + /// True if successful, otherwise false. + /// + /// Length.Parse(""5.5 m"", new CultureInfo(""en-US"")); + /// + /// Name of culture (ex: ""en-US"") to use when parsing number and unit. Defaults to if null. + public static bool TryParse([CanBeNull] string str, [CanBeNull] string cultureName, out {_quantity.Name} result) + {{ + IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); + return QuantityParser.Default.TryParse<{_quantity.Name}, {_unitEnumName}>( + str, + provider, + From, + out result); + }} + + /// + /// Parse a unit string. + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.ParseUnit(""m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// Error parsing string. + public static {_unitEnumName} ParseUnit(string str) + {{ + return ParseUnit(str, null); + }} + + /// + /// Parse a unit string. + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// + /// Length.ParseUnit(""m"", new CultureInfo(""en-US"")); + /// + /// The value of 'str' cannot be null. + /// Error parsing string. + /// Name of culture (ex: ""en-US"") to use when parsing number and unit. Defaults to if null. + public static {_unitEnumName} ParseUnit(string str, [CanBeNull] string cultureName) + {{ + IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); + return UnitParser.Default.Parse<{_unitEnumName}>(str, provider); + }} + + public static bool TryParseUnit(string str, out {_unitEnumName} unit) + {{ + return TryParseUnit(str, null, out unit); + }} + + /// + /// Parse a unit string. + /// + /// String to parse. Typically in the form: {{number}} {{unit}} + /// The parsed unit if successful. + /// True if successful, otherwise false. + /// + /// Length.TryParseUnit(""m"", new CultureInfo(""en-US"")); + /// + /// Name of culture (ex: ""en-US"") to use when parsing number and unit. Defaults to if null. + public static bool TryParseUnit(string str, [CanBeNull] string cultureName, out {_unitEnumName} unit) + {{ + IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); + return UnitParser.Default.TryParse<{_unitEnumName}>(str, provider, out unit); + }} + + #endregion +"); + } + + private void GenerateEqualityAndComparison() + { + Writer.WL($@" + #region Equality / IComparable + + public int CompareTo(object obj) + {{ + if(obj is null) throw new ArgumentNullException(nameof(obj)); + if(!(obj is {_quantity.Name} obj{_quantity.Name})) throw new ArgumentException(""Expected type {_quantity.Name}."", nameof(obj)); + + return CompareTo(obj{_quantity.Name}); + }} + + // 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 + internal int CompareTo({_quantity.Name} other) + {{ + return _value.CompareTo(other.AsBaseNumericType(this.Unit)); + }} + + [Windows.Foundation.Metadata.DefaultOverload] + public override bool Equals(object obj) + {{ + if(obj is null || !(obj is {_quantity.Name} obj{_quantity.Name})) + return false; + + return Equals(obj{_quantity.Name}); + }} + + public bool Equals({_quantity.Name} other) + {{ + return _value.Equals(other.AsBaseNumericType(this.Unit)); + }} + + /// + /// + /// Compare equality to another {_quantity.Name} within the given absolute or relative tolerance. + /// + /// + /// Relative tolerance is defined as the maximum allowable absolute difference between this quantity's value and + /// as a percentage of this quantity's value. will be converted into + /// this quantity's unit for comparison. A relative tolerance of 0.01 means the absolute difference must be within +/- 1% of + /// this quantity's value to be considered equal. + /// + /// In this example, the two quantities will be equal if the value of b is within +/- 1% of a (0.02m or 2cm). + /// + /// var a = Length.FromMeters(2.0); + /// var b = Length.FromInches(50.0); + /// a.Equals(b, 0.01, ComparisonType.Relative); + /// + /// + /// + /// + /// Absolute tolerance is defined as the maximum allowable absolute difference between this quantity's value and + /// as a fixed number in this quantity's unit. will be converted into + /// this quantity's unit for comparison. + /// + /// In this example, the two quantities will be equal if the value of b is within 0.01 of a (0.01m or 1cm). + /// + /// var a = Length.FromMeters(2.0); + /// var b = Length.FromInches(50.0); + /// a.Equals(b, 0.01, ComparisonType.Absolute); + /// + /// + /// + /// + /// Note that it is advised against specifying zero difference, due to the nature + /// of floating point operations and using System.Double internally. + /// + /// + /// The other quantity to compare to. + /// The absolute or relative tolerance value. Must be greater than or equal to 0. + /// The comparison type: either relative or absolute. + /// True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance. + public bool Equals({_quantity.Name} other, double tolerance, ComparisonType comparisonType) + {{ + if(tolerance < 0) + throw new ArgumentOutOfRangeException(""tolerance"", ""Tolerance must be greater than or equal to 0.""); + + double thisValue = (double)this.Value; + double otherValueInThisUnits = other.As(this.Unit); + + return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType); + }} + + /// + /// Returns the hash code for this instance. + /// + /// A hash code for the current {_quantity.Name}. + public override int GetHashCode() + {{ + return new {{ QuantityType, Value, Unit }}.GetHashCode(); + }} + + #endregion +"); + } + + private void GenerateConversionMethods() + { + Writer.WL($@" + #region Conversion Methods + + double IQuantity.As(object unit) => As(({_quantity.Name}Unit)unit); + + /// + /// Convert to the unit representation . + /// + /// Value converted to the specified unit. + public double As({_unitEnumName} unit) + {{ + if(Unit == unit) + return Convert.ToDouble(Value); + + var converted = AsBaseNumericType(unit); + return Convert.ToDouble(converted); + }} + + /// + /// Converts this {_quantity.Name} to another {_quantity.Name} with the unit representation . + /// + /// A {_quantity.Name} with the specified unit. + public {_quantity.Name} ToUnit({_unitEnumName} unit) + {{ + var convertedValue = AsBaseNumericType(unit); + return new {_quantity.Name}(convertedValue, unit); + }} + + /// + /// Converts the current value + unit to the base unit. + /// This is typically the first step in converting from one unit to another. + /// + /// The value in the base unit representation. + private {_valueType} AsBaseUnit() + {{ + switch(Unit) + {{"); + foreach (var unit in _quantity.Units) + { + var func = unit.FromUnitToBaseFunc.Replace("x", "_value"); + Writer.WL($@" + case {_unitEnumName}.{unit.SingularName}: return {func};"); + } + + Writer.WL($@" + default: + throw new NotImplementedException($""Can not convert {{Unit}} to base units.""); + }} + }} + + private {_valueType} AsBaseNumericType({_unitEnumName} unit) + {{ + if(Unit == unit) + return _value; + + var baseUnitValue = AsBaseUnit(); + + switch(unit) + {{"); + foreach (var unit in _quantity.Units) + { + var func = unit.FromBaseToUnitFunc.Replace("x", "baseUnitValue"); + Writer.WL($@" + case {_unitEnumName}.{unit.SingularName}: return {func};"); + } + + Writer.WL(@" + default: + throw new NotImplementedException($""Can not convert {Unit} to {unit}.""); + } + } + + #endregion +"); + } + + private void GenerateToString() + { + Writer.WL($@" + #region ToString Methods + + /// + /// Get default string representation of value and unit. + /// + /// String representation. + public override string ToString() + {{ + return ToString(null); + }} + + /// + /// Get string representation of value and unit. Using two significant digits after radix. + /// + /// String representation. + /// Name of culture (ex: ""en-US"") to use for localization and number formatting. Defaults to if null. + public string ToString([CanBeNull] string cultureName) + {{ + var provider = cultureName; + return ToString(provider, 2); + }} + + /// + /// Get string representation of value and unit. + /// + /// The number of significant digits after the radix point. + /// String representation. + /// Name of culture (ex: ""en-US"") to use for localization and number formatting. Defaults to if null. + public string ToString(string cultureName, int significantDigitsAfterRadix) + {{ + var provider = cultureName; + var value = Convert.ToDouble(Value); + var format = UnitFormatter.GetFormat(value, significantDigitsAfterRadix); + return ToString(provider, format); + }} + + /// + /// Get string representation of value and unit. + /// + /// String format to use. Default: ""{{0:0.##}} {{1}} for value and unit abbreviation respectively."" + /// Arguments for string format. Value and unit are implictly included as arguments 0 and 1. + /// String representation. + /// Name of culture (ex: ""en-US"") to use for localization and number formatting. Defaults to if null. + public string ToString([CanBeNull] string cultureName, [NotNull] string format, [NotNull] params object[] args) + {{ + var provider = GetFormatProviderFromCultureName(cultureName); + if (format == null) throw new ArgumentNullException(nameof(format)); + if (args == null) throw new ArgumentNullException(nameof(args)); + + provider = provider ?? GlobalConfiguration.DefaultCulture; + + var value = Convert.ToDouble(Value); + var formatArgs = UnitFormatter.GetFormatArgs(Unit, value, provider, args); + return string.Format(provider, format, formatArgs); + }} + + #endregion +"); + } + + /// + internal static string GetObsoleteAttributeOrNull(Quantity quantity) => GetObsoleteAttributeOrNull(quantity.ObsoleteText); + + /// + internal static string GetObsoleteAttributeOrNull(Unit unit) => GetObsoleteAttributeOrNull(unit.ObsoleteText); + + /// + /// Returns the Obsolete attribute if ObsoleteText has been defined on the JSON input - otherwise returns empty string + /// It is up to the consumer to wrap any padding/new lines in order to keep to correct indentation formats + /// + private static string GetObsoleteAttributeOrNull(string obsoleteText) => string.IsNullOrWhiteSpace(obsoleteText) + ? null + : $"[System.Obsolete({obsoleteText})]"; + } +} diff --git a/CodeGen/Generators/UnitsNetWrcGen/QuantityTypeGenerator.cs b/CodeGen/Generators/UnitsNetWrcGen/QuantityTypeGenerator.cs new file mode 100644 index 0000000000..582b299d6a --- /dev/null +++ b/CodeGen/Generators/UnitsNetWrcGen/QuantityTypeGenerator.cs @@ -0,0 +1,39 @@ +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetWrcGen +{ + internal class QuantityTypeGenerator : GeneratorBase + { + private readonly Quantity[] _quantities; + + public QuantityTypeGenerator(Quantity[] quantities) + { + _quantities = quantities; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +// ReSharper disable once CheckNamespace +namespace UnitsNet +{ + /// + /// Lists all generated quantities with the same name as the quantity struct type, + /// such as Length, Mass, Force etc. + /// This is useful for populating options in the UI, such as creating a generic conversion + /// tool with inputValue, quantityName, fromUnit and toUnit selectors. + /// + public enum QuantityType + { + Undefined = 0,"); + foreach (var quantity in _quantities) + Writer.WL($@" + {quantity.Name},"); + Writer.WL(@" + } +}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetWrcGen/StaticQuantityGenerator.cs b/CodeGen/Generators/UnitsNetWrcGen/StaticQuantityGenerator.cs new file mode 100644 index 0000000000..133a34850b --- /dev/null +++ b/CodeGen/Generators/UnitsNetWrcGen/StaticQuantityGenerator.cs @@ -0,0 +1,122 @@ +using CodeGen.Helpers; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetWrcGen +{ + internal class StaticQuantityGenerator : GeneratorBase + { + private readonly Quantity[] _quantities; + + public StaticQuantityGenerator(Quantity[] quantities) + { + _quantities = quantities; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +using System; +using System.Linq; +using JetBrains.Annotations; +using UnitsNet.InternalHelpers; +using UnitsNet.Units; + +namespace UnitsNet +{ + /// + /// Dynamically parse or construct quantities when types are only known at runtime. + /// + internal static partial class Quantity + { + /// + /// Try to dynamically construct a quantity. + /// + /// Numeric value. + /// Unit enum value. + /// The resulting quantity if successful, otherwise default. + /// True if successful with assigned the value, otherwise false. + internal static bool TryFrom(double value, Enum unit, out IQuantity quantity) + { + switch (unit) + {"); + foreach (var quantity in _quantities) + { + var quantityName = quantity.Name; + var unitTypeName = $"{quantityName}Unit"; + var unitValue = unitTypeName.ToCamelCase(); + Writer.WL($@" + case {unitTypeName} {unitValue}: + quantity = {quantityName}.From(value, {unitValue}); + return true;"); + } + + Writer.WL($@" + default: + {{ + quantity = default(IQuantity); + return false; + }} + }} + }} + + /// + internal static IQuantity Parse(Type quantityType, string quantityString) => Parse(null, quantityType, quantityString); + + /// + /// Dynamically parse a quantity string representation. + /// + /// The format provider to use for lookup. Defaults to if null. + /// Type of quantity, such as . + /// Quantity string representation, such as ""1.5 kg"". Must be compatible with given quantity type. + /// The parsed quantity. + /// Type must be of type UnitsNet.IQuantity -or- Type is not a known quantity type. + internal static IQuantity Parse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString) + {{ + if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType)) + throw new ArgumentException($""Type {{quantityType}} must be of type UnitsNet.IQuantity.""); + + if (TryParse(formatProvider, quantityType, quantityString, out IQuantity quantity)) return quantity; + + throw new ArgumentException($""Quantity string could not be parsed to quantity {{quantityType}}.""); + }} + + /// + internal static bool TryParse(Type quantityType, string quantityString, out IQuantity quantity) => + TryParse(null, quantityType, quantityString, out quantity); + + /// + /// Try to dynamically parse a quantity string representation. + /// + /// The format provider to use for lookup. Defaults to if null. + /// Type of quantity, such as . + /// Quantity string representation, such as ""1.5 kg"". Must be compatible with given quantity type. + /// The resulting quantity if successful, otherwise default. + /// The parsed quantity. + internal static bool TryParse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString, out IQuantity quantity) + {{ + quantity = default(IQuantity); + + if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType)) + return false; + + var parser = QuantityParser.Default; +"); + foreach (var quantity in _quantities) + { + Writer.WL($@" + if (quantityType == typeof({quantity.Name})) + return parser.TryParse<{quantity.Name}, {quantity.Name}Unit>(quantityString, formatProvider, {quantity.Name}.From, out quantity); +"); + } + + Writer.WL($@" + throw new ArgumentException( + $""Type {{quantityType}} is not a known quantity type. Did you pass in a third-party quantity type defined outside UnitsNet library?""); + }} + }} +}}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetWrcGen/UnitAbbreviationsCacheGenerator.cs b/CodeGen/Generators/UnitsNetWrcGen/UnitAbbreviationsCacheGenerator.cs new file mode 100644 index 0000000000..5695ad777a --- /dev/null +++ b/CodeGen/Generators/UnitsNetWrcGen/UnitAbbreviationsCacheGenerator.cs @@ -0,0 +1,58 @@ +using System.Linq; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetWrcGen +{ + internal class UnitAbbreviationsCacheGenerator : GeneratorBase + { + private readonly Quantity[] _quantities; + + public UnitAbbreviationsCacheGenerator(Quantity[] quantities) + { + _quantities = quantities; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL(@" +using System; +using UnitsNet.Units; + +// ReSharper disable RedundantCommaInArrayInitializer +// ReSharper disable once CheckNamespace +namespace UnitsNet +{ + public sealed partial class UnitAbbreviationsCache + { + private static readonly (string CultureName, Type UnitType, int UnitValue, string[] UnitAbbreviations)[] GeneratedLocalizations + = new [] + {"); + foreach (var quantity in _quantities) + { + var unitEnumName = $"{quantity.Name}Unit"; + + foreach (var unit in quantity.Units) + { + foreach (var localization in unit.Localization) + { + var cultureName = localization.Culture; + + // All units must have a unit abbreviation, so fallback to "" for units with no abbreviations defined in JSON + var abbreviationParams = localization.Abbreviations.Any() + ? string.Join(", ", localization.Abbreviations.Select(abbr => $"\"{abbr}\"")) + : "\"\""; + Writer.WL($@" + (""{cultureName}"", typeof({unitEnumName}), (int){unitEnumName}.{unit.SingularName}, new string[]{{{abbreviationParams}}}),"); + } + } + } + + Writer.WL(@" + }; + } +}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetWrcGen/UnitTypeGenerator.cs b/CodeGen/Generators/UnitsNetWrcGen/UnitTypeGenerator.cs new file mode 100644 index 0000000000..939d947a17 --- /dev/null +++ b/CodeGen/Generators/UnitsNetWrcGen/UnitTypeGenerator.cs @@ -0,0 +1,61 @@ +using CodeGen.Helpers; +using CodeGen.JsonTypes; + +namespace CodeGen.Generators.UnitsNetWrcGen +{ + internal class UnitTypeGenerator : GeneratorBase + { + private readonly Quantity _quantity; + private readonly string _unitEnumName; + + public UnitTypeGenerator(Quantity quantity) + { + _quantity = quantity; + _unitEnumName = $"{quantity.Name}Unit"; + } + + public override string Generate() + { + Writer.WL(GeneratedFileHeader); + Writer.WL($@" +// ReSharper disable once CheckNamespace +namespace UnitsNet.Units +{{ + // Disable missing XML comment warnings for the generated unit enums. + #pragma warning disable 1591 + + public enum {_unitEnumName} + {{ + Undefined = 0,"); + + foreach (var unit in _quantity.Units) + { + if (unit.XmlDocSummary.HasText()) + { + Writer.WL(); + Writer.WL($@" + /// + /// {unit.XmlDocSummary} + /// "); + } + + if (unit.XmlDocRemarks.HasText()) + { + Writer.WL($@" + /// {unit.XmlDocRemarks}"); + } + + Writer.WLIfText(2, UnitsNetGen.QuantityGenerator.GetObsoleteAttributeOrNull(unit)); + Writer.WL($@" + {unit.SingularName},"); + } + + Writer.WL($@" + }} + + #pragma warning restore 1591 +}}"); + return Writer.ToString(); + } + } +} diff --git a/CodeGen/Generators/UnitsNetWrcGenerator.cs b/CodeGen/Generators/UnitsNetWrcGenerator.cs new file mode 100644 index 0000000000..86c17de1d6 --- /dev/null +++ b/CodeGen/Generators/UnitsNetWrcGenerator.cs @@ -0,0 +1,250 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using CodeGen.Helpers; +using CodeGen.JsonTypes; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serilog; +using QuantityGenerator = CodeGen.Generators.UnitsNetWrcGen.QuantityGenerator; +using QuantityTypeGenerator = CodeGen.Generators.UnitsNetWrcGen.QuantityTypeGenerator; +using StaticQuantityGenerator = CodeGen.Generators.UnitsNetWrcGen.StaticQuantityGenerator; +using UnitAbbreviationsCacheGenerator = CodeGen.Generators.UnitsNetWrcGen.UnitAbbreviationsCacheGenerator; +using UnitTypeGenerator = CodeGen.Generators.UnitsNetWrcGen.UnitTypeGenerator; + +namespace CodeGen.Generators +{ + internal static class UnitsNetWrcGenerator + { + private const int AlignPad = 35; + + private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings + { + // Don't override the C# default assigned values if no value is set in JSON + NullValueHandling = NullValueHandling.Ignore + }; + + public static void Generate(DirectoryInfo repositoryRoot) + { + if (repositoryRoot == null) throw new ArgumentNullException(nameof(repositoryRoot)); + var root = repositoryRoot.FullName; + var outputDir = $"{root}/UnitsNet.WindowsRuntimeComponent/GeneratedCode"; + + var templatesDir = Path.Combine(root, "Common/UnitDefinitions"); + var jsonFiles = Directory.GetFiles(templatesDir, "*.json"); + var quantities = jsonFiles.Select(ParseQuantityFile).ToArray(); + + foreach (Quantity quantity in quantities) + { + var sb = new StringBuilder($"{quantity.Name}:".PadRight(AlignPad)); + GenerateQuantity(sb, quantity, $"{outputDir}/Quantities/{quantity.Name}.WindowsRuntimeComponent.g.cs"); + GenerateUnitType(sb, quantity, $"{outputDir}/Units/{quantity.Name}Unit.g.cs"); + Log.Information(sb.ToString()); + } + + Log.Information(""); + GenerateUnitAbbreviationsCache(quantities, $"{outputDir}/UnitAbbreviationsCache.g.cs"); + GenerateQuantityType(quantities, $"{outputDir}/QuantityType.g.cs"); + GenerateStaticQuantity(quantities, $"{outputDir}/Quantity.g.cs"); + + var unitCount = quantities.SelectMany(q => q.Units).Count(); + Log.Information(""); + Log.Information($"Total of {unitCount} units and {quantities.Length} quantities."); + Log.Information(""); + } + + private static Quantity ParseQuantityFile(string jsonFile) + { + var quantity = JsonConvert.DeserializeObject(File.ReadAllText(jsonFile, Encoding.UTF8), JsonSerializerSettings); + AddPrefixUnits(quantity); + FixConversionFunctionsForDecimalValueTypes(quantity); + OrderUnitsByName(quantity); + return quantity; + } + + private static void GenerateQuantity(StringBuilder sb, Quantity quantity, string filePath) + { + string content = new QuantityGenerator(quantity).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + sb.Append("quantity(OK) "); + } + + private static void GenerateUnitType(StringBuilder sb, Quantity quantity, string filePath) + { + string content = new UnitTypeGenerator(quantity).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + sb.Append("unit(OK) "); + } + + private static void GenerateUnitAbbreviationsCache(Quantity[] quantities, string filePath) + { + string content = new UnitAbbreviationsCacheGenerator(quantities).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + Log.Information("UnitAbbreviationsCache.g.cs: ".PadRight(AlignPad) + "(OK)"); + } + + private static void GenerateQuantityType(Quantity[] quantities, string filePath) + { + string content = new QuantityTypeGenerator(quantities).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + Log.Information("QuantityType.g.cs: ".PadRight(AlignPad) + "(OK)"); + } + + private static void GenerateStaticQuantity(Quantity[] quantities, string filePath) + { + string content = new StaticQuantityGenerator(quantities).Generate(); + File.WriteAllText(filePath, content, Encoding.UTF8); + Log.Information("Quantity.g.cs: ".PadRight(AlignPad) + "(OK)"); + } + + private static void OrderUnitsByName(Quantity quantity) + { + quantity.Units = quantity.Units.OrderBy(u => u.SingularName).ToArray(); + } + + private static void FixConversionFunctionsForDecimalValueTypes(Quantity quantity) + { + foreach (Unit u in quantity.Units) + { + // Use decimal for internal calculations if base type is not double, such as for long or int. + if (string.Equals(quantity.BaseType, "decimal", StringComparison.OrdinalIgnoreCase)) + { + // Change any double literals like "1024d" to decimal literals "1024m" + u.FromUnitToBaseFunc = u.FromUnitToBaseFunc.Replace("d", "m"); + u.FromBaseToUnitFunc = u.FromBaseToUnitFunc.Replace("d", "m"); + } + } + } + + private static void AddPrefixUnits(Quantity quantity) + { + var unitsToAdd = new List(); + foreach (Unit unit in quantity.Units) + { + // "Kilo", "Nano" etc. + for (var prefixIndex = 0; prefixIndex < unit.Prefixes.Length; prefixIndex++) + { + Prefix prefix = unit.Prefixes[prefixIndex]; + PrefixInfo prefixInfo = PrefixInfo.Entries[prefix]; + + unitsToAdd.Add(new Unit + { + SingularName = $"{prefix}{unit.SingularName.ToCamelCase()}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter" + PluralName = $"{prefix}{unit.PluralName.ToCamelCase()}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter" + BaseUnits = null, // Can we determine this somehow? + FromBaseToUnitFunc = $"({unit.FromBaseToUnitFunc}) / {prefixInfo.Factor}", + FromUnitToBaseFunc = $"({unit.FromUnitToBaseFunc}) * {prefixInfo.Factor}", + Localization = GetLocalizationForPrefixUnit(unit, prefixIndex, prefixInfo, quantity.Name), + }); + } + } + + quantity.Units = quantity.Units.Concat(unitsToAdd).ToArray(); + } + + private static Localization[] GetLocalizationForPrefixUnit(Unit unit, int prefixIndex, PrefixInfo prefixInfo, string quantityName) + { + string[] GetUnitAbbreviationsForPrefix(Localization loc) + { + // If no custom abbreviations are specified, prepend the default prefix to each unit abbreviation: kilo ("k") + meter ("m") => kilometer ("km") + if (loc.AbbreviationsWithPrefixes == null || !loc.AbbreviationsWithPrefixes.Any()) + { + string prefix = prefixInfo.Abbreviation; + return loc.Abbreviations.Select(unitAbbreviation => $"{prefix}{unitAbbreviation}").ToArray(); + } + + /* + Prepend prefix to all abbreviations of a unit. + Some languages, like Russian, you can't simply prepend "k" for kilo prefix, so the prefix abbreviations must be explicitly defined + with AbbreviationsWithPrefixes. + + Example 1 - Torque.Newtonmeter has only a single abbreviation in Russian, so AbbreviationsWithPrefixes is an array of strings mapped to each prefix + + { + "SingularName": "NewtonMeter", + "PluralName": "NewtonMeters", + "FromUnitToBaseFunc": "x", + "FromBaseToUnitFunc": "x", + "Prefixes": [ "Kilo", "Mega" ], + "Localization": [ + { + "Culture": "en-US", + "Abbreviations": [ "N·m" ] + }, + { + "Culture": "ru-RU", + "Abbreviations": [ "Н·м" ], + "AbbreviationsWithPrefixes": [ "кН·м", "МН·м" ] + } + ] + }, + + Example 2 - Duration.Second has 3 prefixes and 2 abbreviations in Russian, so AbbreviationsWithPrefixes is an array of 3 items where each + represents the unit abbreviations for that prefix - typically a variant of those in "Abbreviations", but the counts don't have to match. + + { + "SingularName": "Second", + "PluralName": "Seconds", + "BaseUnits": { + "T": "Second" + }, + "FromUnitToBaseFunc": "x", + "FromBaseToUnitFunc": "x", + "Prefixes": [ "Nano", "Micro", "Milli" ], + "Localization": [ + { + "Culture": "en-US", + "Abbreviations": [ "s", "sec", "secs", "second", "seconds" ] + }, + { + "Culture": "ru-RU", + "Abbreviations": [ "с", "сек" ], + "AbbreviationsWithPrefixes": [ ["нс", "нсек"], ["мкс", "мксек"], ["мс", "мсек"] ] + } + ] + } + */ + + EnsureValidAbbreviationsWithPrefixes(loc, unit, quantityName); + JToken abbreviationsForPrefix = loc.AbbreviationsWithPrefixes[prefixIndex]; + switch (abbreviationsForPrefix.Type) + { + case JTokenType.Array: + return abbreviationsForPrefix.ToObject(); + case JTokenType.String: + return new[] {abbreviationsForPrefix.ToObject()}; + default: + throw new NotSupportedException("Expect AbbreviationsWithPrefixes to be an array of strings or string arrays."); + } + } + + Localization WithPrefixes(Localization loc) + { + string[] unitAbbreviationsForPrefix = GetUnitAbbreviationsForPrefix(loc); + + return new Localization + { + Culture = loc.Culture, + Abbreviations = unitAbbreviationsForPrefix, + }; + } + + return unit.Localization.Select(WithPrefixes).ToArray(); + } + + private static void EnsureValidAbbreviationsWithPrefixes(Localization localization, Unit unit, string quantityName) + { + if (localization.AbbreviationsWithPrefixes.Length > 0 && + localization.AbbreviationsWithPrefixes.Length != unit.Prefixes.Length) + { + throw new InvalidDataException( + $"The Prefixes array length {unit.Prefixes.Length} does not match Localization.AbbreviationsWithPrefixes array length {localization.AbbreviationsWithPrefixes.Length} for {quantityName}.{unit.SingularName}"); + } + } + } +} diff --git a/CodeGen/Helpers/MyTextWriter.cs b/CodeGen/Helpers/MyTextWriter.cs new file mode 100644 index 0000000000..3fae829a09 --- /dev/null +++ b/CodeGen/Helpers/MyTextWriter.cs @@ -0,0 +1,109 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System.Linq; +using System.Text; + +namespace CodeGen.Helpers +{ + internal class MyTextWriter + { + private readonly StringBuilder _sb = new StringBuilder(); + private string _currentIndentationString; + private int _indentLevel; + + public MyTextWriter(string indentString = " ", int initialIndentLevel = 0) + { + IndentString = indentString; + IndentLevel = initialIndentLevel; + } + + public string IndentString { get; } + + public int IndentLevel + { + get => _indentLevel; + set + { + _indentLevel = value; + _currentIndentationString = GetIndent(_indentLevel); + } + } + + private string GetIndent(int indentLevel) + { + return string.Join(string.Empty, Enumerable.Repeat(IndentString, indentLevel)); + } + + /// + /// Write line with current indent. Trims preceding newline if any, to simplify code formatting when calling this method. + /// + /// The indent level to prepend the text with. + /// The text to write + public void WL(int indentLevel, string text = "") + { + _sb.Append(GetIndent(indentLevel)); + _sb.AppendLine(text.TrimStart('\r', '\n')); + } + + /// + /// Write line with current indent. Trims preceding newline if any, to simplify code formatting when calling this method. + /// + /// The text to write + public void WL(string text = "") + { + _sb.Append(_currentIndentationString); + _sb.AppendLine(text.TrimStart('\r', '\n')); + } + + /// + /// Write with current indent, but no newline. Trims preceding newline if any, to simplify code formatting when calling this method. + /// + /// The text to write + public void W(string text = "") + { + _sb.Append(_currentIndentationString); + _sb.Append(text.TrimStart('\r', '\n')); + } + + /// + /// Append text without indentation and without newline. + /// + /// The text to write + public void Append(string text = "") + { + _sb.Append(text); + } + + /// + /// Returns the text written so far. + /// + /// + public override string ToString() + { + return _sb.ToString(); + } + + /// + /// Write line with current ident only if actually contains text and not just whitespace. + /// + /// The level of indentation to prepend to the text. + /// The text to write. + public void WLIfText(int indentLevel, string text) + { + if (string.IsNullOrWhiteSpace(text)) return; + WL(indentLevel, text); + } + + /// + /// Write line with current ident only if is true. + /// + /// The condition. + /// The text to write. + public void WLCondition(bool condition, string text) + { + if (condition) + WL(text); + } + } +} diff --git a/CodeGen/Helpers/StringExtensions.cs b/CodeGen/Helpers/StringExtensions.cs new file mode 100644 index 0000000000..9091491889 --- /dev/null +++ b/CodeGen/Helpers/StringExtensions.cs @@ -0,0 +1,21 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +namespace CodeGen.Helpers +{ + internal static class StringExtensions + { + /// + /// Returns true if string is not null and not whitespace. + /// + public static bool HasText(this string str) => !string.IsNullOrWhiteSpace(str); + + /// + /// Example: "Kilo" + ToCamelCase("NewtonPerMeter") => "KilonewtonPerMeter" + /// + public static string ToCamelCase(this string str) + { + return char.ToLowerInvariant(str[0]) + str.Substring(1); + } + } +} diff --git a/CodeGen/JsonTypes/BaseDimensions.cs b/CodeGen/JsonTypes/BaseDimensions.cs new file mode 100644 index 0000000000..a6caad21fd --- /dev/null +++ b/CodeGen/JsonTypes/BaseDimensions.cs @@ -0,0 +1,29 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +namespace CodeGen.JsonTypes +{ + internal class BaseDimensions + { + // 0649 Field is never assigned to +#pragma warning disable 0649 + + /// AmountOfSubstance. + public int N = 0; + /// ElectricCurrent. + public int I = 0; + /// Length. + public int L = 0; + /// LuminousIntensity. + public int J = 0; + /// Mass. + public int M = 0; + /// Temperature. + public int Θ = 0; + /// Time. + public int T = 0; + + // 0649 Field is never assigned to +#pragma warning restore 0649 + } +} diff --git a/CodeGen/JsonTypes/BaseUnits.cs b/CodeGen/JsonTypes/BaseUnits.cs new file mode 100644 index 0000000000..46f0887811 --- /dev/null +++ b/CodeGen/JsonTypes/BaseUnits.cs @@ -0,0 +1,29 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +namespace CodeGen.JsonTypes +{ + internal class BaseUnits + { + // 0649 Field is never assigned to +#pragma warning disable 0649 + + /// AmountOfSubstance. + public string N; + /// ElectricCurrent. + public string I; + /// Length. + public string L; + /// LuminousIntensity. + public string J; + /// Mass. + public string M; + /// Temperature. + public string Θ; + /// Time. + public string T; + + // 0649 Field is never assigned to +#pragma warning restore 0649 + } +} diff --git a/CodeGen/JsonTypes/Localization.cs b/CodeGen/JsonTypes/Localization.cs new file mode 100644 index 0000000000..0419ca2e4f --- /dev/null +++ b/CodeGen/JsonTypes/Localization.cs @@ -0,0 +1,21 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using Newtonsoft.Json.Linq; + +namespace CodeGen.JsonTypes +{ + internal class Localization + { + // 0649 Field is never assigned to +#pragma warning disable 0649 + + public string[] Abbreviations = Array.Empty(); + public JToken[] AbbreviationsWithPrefixes; + public string Culture; + + // 0649 Field is never assigned to +#pragma warning restore 0649 + } +} diff --git a/CodeGen/JsonTypes/Prefix.cs b/CodeGen/JsonTypes/Prefix.cs new file mode 100644 index 0000000000..62d106bb39 --- /dev/null +++ b/CodeGen/JsonTypes/Prefix.cs @@ -0,0 +1,35 @@ +namespace CodeGen.JsonTypes +{ + internal enum Prefix + { + // SI prefixes + Yocto, + Zepto, + Atto, + Femto, + Pico, + Nano, + Micro, + Milli, + Centi, + Deci, + Deca, + Hecto, + Kilo, + Mega, + Giga, + Tera, + Peta, + Exa, + Zetta, + Yotta, + + // Binary prefixes + Kibi, + Mebi, + Gibi, + Tebi, + Pebi, + Exbi + } +} \ No newline at end of file diff --git a/CodeGen/JsonTypes/Quantity.cs b/CodeGen/JsonTypes/Quantity.cs new file mode 100644 index 0000000000..0888bf8464 --- /dev/null +++ b/CodeGen/JsonTypes/Quantity.cs @@ -0,0 +1,29 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Collections.Generic; + +namespace CodeGen.JsonTypes +{ + internal class Quantity + { + // 0649 Field is never assigned to +#pragma warning disable 0649 + + public BaseDimensions BaseDimensions = new BaseDimensions(); // Default to empty + public string BaseType = "double"; // TODO Rename to ValueType + public string BaseUnit; // TODO Rename to DefaultUnit or IntermediateConversionUnit to avoid confusion with Unit.BaseUnits + public bool GenerateArithmetic = true; + public bool Logarithmic = false; + public int LogarithmicScalingFactor = 1; + public string Name; + public Unit[] Units = Array.Empty(); + public string XmlDocRemarks; + public string XmlDoc; // TODO Rename to XmlDocSummary + public string ObsoleteText; + + // 0649 Field is never assigned to +#pragma warning restore 0649 + } +} diff --git a/CodeGen/JsonTypes/Unit.cs b/CodeGen/JsonTypes/Unit.cs new file mode 100644 index 0000000000..b6d03feb9a --- /dev/null +++ b/CodeGen/JsonTypes/Unit.cs @@ -0,0 +1,27 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; + +namespace CodeGen.JsonTypes +{ + internal class Unit + { + // 0649 Field is never assigned to +#pragma warning disable 0649 + + public BaseUnits BaseUnits; + public string FromBaseToUnitFunc; + public string FromUnitToBaseFunc; + public Localization[] Localization = Array.Empty(); + public string PluralName; + public Prefix[] Prefixes = Array.Empty(); + public string SingularName; + public string XmlDocRemarks; + public string XmlDocSummary; + public string ObsoleteText; + + // 0649 Field is never assigned to +#pragma warning restore 0649 + } +} diff --git a/CodeGen/PrefixInfo.cs b/CodeGen/PrefixInfo.cs new file mode 100644 index 0000000000..48eae0f539 --- /dev/null +++ b/CodeGen/PrefixInfo.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using CodeGen.JsonTypes; + +namespace CodeGen +{ + /// + /// Information about a unit prefix and a static dictionary to look up prefixes. + /// + internal class PrefixInfo + { + /// + /// The unit prefix abbreviation, such as "k" for kilo or "m" for milli. + /// + public string Abbreviation { get; } + + /// + /// C# expression for the multiplier to prefix the conversion function. + /// + /// Kilo has "1e3" in order to multiply by 1000. + public string Factor { get; } + + public static readonly IReadOnlyDictionary Entries = new Dictionary + { + // NOTE: Need to append 'd' suffix for double in order to later search/replace "d" with "m" + // when creating decimal conversion functions in CodeGen.Generator.FixConversionFunctionsForDecimalValueTypes. + + // SI prefixes + { Prefix.Yocto, new PrefixInfo("y", "1e-24d") }, + { Prefix.Zepto, new PrefixInfo("z", "1e-21d") }, + { Prefix.Atto, new PrefixInfo("a", "1e-18d") }, + { Prefix.Femto, new PrefixInfo("f", "1e-15d") }, + { Prefix.Pico, new PrefixInfo("p", "1e-12d") }, + { Prefix.Nano, new PrefixInfo("n", "1e-9d") }, + { Prefix.Micro, new PrefixInfo("µ", "1e-6d") }, + { Prefix.Milli, new PrefixInfo("m", "1e-3d") }, + { Prefix.Centi, new PrefixInfo("c", "1e-2d") }, + { Prefix.Deci, new PrefixInfo("d", "1e-1d") }, + { Prefix.Deca, new PrefixInfo("da", "1e1d") }, + { Prefix.Hecto, new PrefixInfo("h", "1e2d") }, + { Prefix.Kilo, new PrefixInfo("k", "1e3d") }, + { Prefix.Mega, new PrefixInfo("M", "1e6d") }, + { Prefix.Giga, new PrefixInfo("G", "1e9d") }, + { Prefix.Tera, new PrefixInfo("T", "1e12d") }, + { Prefix.Peta, new PrefixInfo("P", "1e15d") }, + { Prefix.Exa, new PrefixInfo("E", "1e18d") }, + { Prefix.Zetta, new PrefixInfo("Z", "1e21d") }, + { Prefix.Yotta, new PrefixInfo("Y", "1e24d") }, + + // Binary prefixes + { Prefix.Kibi, new PrefixInfo("Ki", $"1024d") }, + { Prefix.Mebi, new PrefixInfo("Mi", $"(1024d * 1024)") }, + { Prefix.Gibi, new PrefixInfo("Gi", $"(1024d * 1024 * 1024)") }, + { Prefix.Tebi, new PrefixInfo("Ti", $"(1024d * 1024 * 1024 * 1024)") }, + { Prefix.Pebi, new PrefixInfo("Pi", $"(1024d * 1024 * 1024 * 1024 * 1024)") }, + { Prefix.Exbi, new PrefixInfo("Ei", $"(1024d * 1024 * 1024 * 1024 * 1024 * 1024)") }, + }; + + private PrefixInfo(string abbreviation, string factor) + { + Abbreviation = abbreviation; + Factor = factor; + } + } +} diff --git a/CodeGen/Program.cs b/CodeGen/Program.cs new file mode 100644 index 0000000000..baa428c7c8 --- /dev/null +++ b/CodeGen/Program.cs @@ -0,0 +1,94 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using CodeGen.Generators; +using Serilog; +using Serilog.Events; + +namespace CodeGen +{ + class Program + { + /// + /// Code generator for Units.NET. + /// Reads unit definitions from JSON files and outputs C# files in GeneratedCode folders: + /// + /// Quantity types (Length, Mass, ...) + /// enum type (QuantityType.Length, QuantityType.Mass, ...) + /// type + /// + /// Test stubs for testing conversion functions of all units, to be fleshed out by a human later + /// Unit enum types (LengthUnit, MassUnit, ...) + /// + /// + /// + /// System.CommandLine.Dragonfruit based Main method, where CLI arguments are parsed and passed directly to this method. + /// See https://github.com/dotnet/command-line-api/ + /// + /// Verbose output? Defaults to false. + /// The repository root directory, defaults to searching parent directories for UnitsNet.sln. + /// Skip generate UnitsNet.WindowsRuntimeComponent? Defaults to false. + static int Main(bool verbose = false, DirectoryInfo repositoryRoot = null, bool skipWrc = false) + { + Log.Logger = new LoggerConfiguration() + .WriteTo + .Console(restrictedToMinimumLevel: verbose ? LogEventLevel.Verbose : LogEventLevel.Information) + .CreateLogger(); + + try + { + if (repositoryRoot == null) + { + var executableParentDir = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + Log.Verbose($"Executable dir: {executableParentDir}"); + + if (!TryFindRepositoryRoot(executableParentDir, out repositoryRoot)) + { + throw new Exception($"Unable to find repository root in directory hierarchy: {executableParentDir}"); + } + } + + var sw = Stopwatch.StartNew(); + Log.Information($"Units.NET code generator {Assembly.GetExecutingAssembly().GetName().Version}", ConsoleColor.Green); + if (verbose) + { + Log.Debug($"verbose: {true}", ConsoleColor.Blue); + } + + UnitsNetGenerator.Generate(repositoryRoot); + + if (!skipWrc) + UnitsNetWrcGenerator.Generate(repositoryRoot); + + Log.Information($"Completed in {sw.ElapsedMilliseconds} ms!", ConsoleColor.Green); + return 0; + } + catch (Exception e) + { + Log.Error(e, "Unexpected error."); + return 1; + } + } + + private static bool TryFindRepositoryRoot(DirectoryInfo searchFromDir, out DirectoryInfo repoRootDir) + { + for (var dir = searchFromDir; dir != null; dir = dir.Parent) + { + if (dir.GetFiles("UnitsNet.sln").Any()) + { + repoRootDir = dir; + Log.Verbose($"Found repo root: {dir}"); + return true; + } + + Log.Verbose($"Not repo root: {dir}"); + } + + Log.Verbose($"Giving up finding repo root."); + repoRootDir = null; + return false; + } + } +} diff --git a/CodeGen/README.md b/CodeGen/README.md new file mode 100644 index 0000000000..121c50bd82 --- /dev/null +++ b/CodeGen/README.md @@ -0,0 +1,33 @@ +# Units.NET Code Generator +This tool will replace the PowerShell scripts for generating source code. +It is faster, easier to debug and more familiar to C# developers. + +## Generating code +Compile and run: +```cmd +cd /dev/UnitsNet/CodeGen +dotnet run +``` + +Alternatively, build the .exe and run it: +```cmd +cd /dev/UnitsNet/CodeGen +dotnet publish -c Release -r win10-x64 +cd ../Artifacts/CodeGen/netcoreapp2.1/win10-x64/ +CodeGen.exe +``` + +## Bonus: .NET CLI Parameter Suggestions +The new, experimental DragonFruit .NET Core console app model has a suggestion API to query parameters of .exe files in order to suggest or auto-complete like PowerShell does. +To get parameter suggestions, follow the [instructions](https://github.com/dotnet/command-line-api/wiki/Features-overview#suggestions). + +PowerShell: +```powershell +cd /dev/UnitsNet/CodeGen +dotnet publish -c Release -r win10-x64 CodeGen +cd ../Artifacts/CodeGen/netcoreapp2.1/win10-x64/ +CodeGen.exe --ver +``` + +Hit TAB and it should now suggest `--version` and `--verbose` parameters. +This should work with any .exe that is compiled with Dragonfruit's app model. diff --git a/UnitsNet.Tests/GeneratedCode/InformationTestsBase.g.cs b/UnitsNet.Tests/GeneratedCode/InformationTestsBase.g.cs index 41d1faad3b..9d1d4a362f 100644 --- a/UnitsNet.Tests/GeneratedCode/InformationTestsBase.g.cs +++ b/UnitsNet.Tests/GeneratedCode/InformationTestsBase.g.cs @@ -96,7 +96,6 @@ public void Ctor_WithUndefinedUnit_ThrowsArgumentException() Assert.Throws(() => new Information((decimal)0.0, InformationUnit.Undefined)); } - [Fact] public void BitToInformationUnits() { @@ -160,7 +159,6 @@ public void FromValueAndUnit() AssertEx.EqualTolerance(1, Information.From(1, InformationUnit.Terabyte).Terabytes, TerabytesTolerance); } - [Fact] public void As() { diff --git a/UnitsNet.Tests/GeneratedCode/LevelTestsBase.g.cs b/UnitsNet.Tests/GeneratedCode/LevelTestsBase.g.cs index 48c2a61dea..a0bb62b88a 100644 --- a/UnitsNet.Tests/GeneratedCode/LevelTestsBase.g.cs +++ b/UnitsNet.Tests/GeneratedCode/LevelTestsBase.g.cs @@ -136,7 +136,6 @@ public void LogarithmicArithmeticOperators() protected abstract void AssertLogarithmicSubtraction(); - [Fact] public void ComparisonOperators() { diff --git a/UnitsNet.WindowsRuntimeComponent/GeneratedCode/UnitAbbreviationsCache.g.cs b/UnitsNet.WindowsRuntimeComponent/GeneratedCode/UnitAbbreviationsCache.g.cs index fc54d544e3..e5d56fbb6d 100644 --- a/UnitsNet.WindowsRuntimeComponent/GeneratedCode/UnitAbbreviationsCache.g.cs +++ b/UnitsNet.WindowsRuntimeComponent/GeneratedCode/UnitAbbreviationsCache.g.cs @@ -71,18 +71,15 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Degree, new string[]{"°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Gradian, new string[]{"g"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Gradian, new string[]{"g"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µ°"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µdeg"}), + ("en-US", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µ°", "µdeg"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µ°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Microradian, new string[]{"µrad"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Microradian, new string[]{"µрад"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"m°"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"mdeg"}), + ("en-US", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"m°", "mdeg"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"m°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Milliradian, new string[]{"mrad"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Milliradian, new string[]{"mрад"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"n°"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"ndeg"}), + ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"n°", "ndeg"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"n°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanoradian, new string[]{"nrad"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Nanoradian, new string[]{"nрад"}), @@ -129,41 +126,29 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(AreaMomentOfInertiaUnit), (int)AreaMomentOfInertiaUnit.MillimeterToTheFourth, new string[]{"mm⁴", "mm^4"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.BitPerSecond, new string[]{"bit/s", "bps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.BytePerSecond, new string[]{"B/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabitPerSecond, new string[]{"Ebit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabitPerSecond, new string[]{"Ebps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabitPerSecond, new string[]{"Ebit/s", "Ebps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabytePerSecond, new string[]{"EB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibitPerSecond, new string[]{"Eibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibitPerSecond, new string[]{"Eibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibitPerSecond, new string[]{"Eibit/s", "Eibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibytePerSecond, new string[]{"EiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibitPerSecond, new string[]{"Gibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibitPerSecond, new string[]{"Gibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibitPerSecond, new string[]{"Gibit/s", "Gibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibytePerSecond, new string[]{"GiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabitPerSecond, new string[]{"Gbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabitPerSecond, new string[]{"Gbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabitPerSecond, new string[]{"Gbit/s", "Gbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabytePerSecond, new string[]{"GB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibitPerSecond, new string[]{"Kibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibitPerSecond, new string[]{"Kibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibitPerSecond, new string[]{"Kibit/s", "Kibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibytePerSecond, new string[]{"KiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobitPerSecond, new string[]{"kbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobitPerSecond, new string[]{"kbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobitPerSecond, new string[]{"kbit/s", "kbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobytePerSecond, new string[]{"kB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibitPerSecond, new string[]{"Mibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibitPerSecond, new string[]{"Mibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibitPerSecond, new string[]{"Mibit/s", "Mibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibytePerSecond, new string[]{"MiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabitPerSecond, new string[]{"Mbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabitPerSecond, new string[]{"Mbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabitPerSecond, new string[]{"Mbit/s", "Mbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabytePerSecond, new string[]{"MB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibitPerSecond, new string[]{"Pibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibitPerSecond, new string[]{"Pibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibitPerSecond, new string[]{"Pibit/s", "Pibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibytePerSecond, new string[]{"PiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabitPerSecond, new string[]{"Pbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabitPerSecond, new string[]{"Pbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabitPerSecond, new string[]{"Pbit/s", "Pbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabytePerSecond, new string[]{"PB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibitPerSecond, new string[]{"Tibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibitPerSecond, new string[]{"Tibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibitPerSecond, new string[]{"Tibit/s", "Tibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibytePerSecond, new string[]{"TiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabitPerSecond, new string[]{"Tbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabitPerSecond, new string[]{"Tbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabitPerSecond, new string[]{"Tbit/s", "Tbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabytePerSecond, new string[]{"TB/s"}), ("en-US", typeof(BrakeSpecificFuelConsumptionUnit), (int)BrakeSpecificFuelConsumptionUnit.GramPerKiloWattHour, new string[]{"g/kWh"}), ("en-US", typeof(BrakeSpecificFuelConsumptionUnit), (int)BrakeSpecificFuelConsumptionUnit.KilogramPerJoule, new string[]{"kg/J"}), @@ -226,37 +211,16 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Day, new string[]{"д"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Hour, new string[]{"h", "hr", "hrs", "hour", "hours"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Hour, new string[]{"ч"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µsec"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µsecs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µsecond"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µseconds"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мкс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мксек"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мкс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мксек"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"ms"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"msec"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"msecs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"msecond"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"mseconds"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мсек"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мсек"}), + ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µs", "µsec", "µsecs", "µsecond", "µseconds"}), + ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мкс", "мксек"}), + ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"ms", "msec", "msecs", "msecond", "mseconds"}), + ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мс", "мсек"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Minute, new string[]{"m", "min", "minute", "minutes"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Minute, new string[]{"мин"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Month30, new string[]{"mo", "month", "months"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Month30, new string[]{"месяц"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"ns"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nsec"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nsecs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nsecond"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nseconds"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нсек"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нсек"}), + ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"ns", "nsec", "nsecs", "nsecond", "nseconds"}), + ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нс", "нсек"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Second, new string[]{"s", "sec", "secs", "second", "seconds"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Second, new string[]{"с", "сек"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Week, new string[]{"wk", "week", "weeks"}), @@ -592,18 +556,14 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(MassUnit), (int)MassUnit.Hectogram, new string[]{"гг"}), ("en-US", typeof(MassUnit), (int)MassUnit.Kilogram, new string[]{"kg"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Kilogram, new string[]{"кг"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klb"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klbs"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klbm"}), + ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klb", "klbs", "klbm"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"kфунт"}), ("en-US", typeof(MassUnit), (int)MassUnit.Kilotonne, new string[]{"kt"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Kilotonne, new string[]{"кт"}), ("en-US", typeof(MassUnit), (int)MassUnit.LongHundredweight, new string[]{"cwt"}), ("en-US", typeof(MassUnit), (int)MassUnit.LongTon, new string[]{"long tn"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.LongTon, new string[]{"тонна большая"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlb"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlbs"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlbm"}), + ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlb", "Mlbs", "Mlbm"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mфунт"}), ("en-US", typeof(MassUnit), (int)MassUnit.Megatonne, new string[]{"Mt"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Megatonne, new string[]{"Мт"}), @@ -760,20 +720,14 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(MolarEntropyUnit), (int)MolarEntropyUnit.JoulePerMoleKelvin, new string[]{"J/(mol*K)"}), ("en-US", typeof(MolarEntropyUnit), (int)MolarEntropyUnit.KilojoulePerMoleKelvin, new string[]{"kJ/(mol*K)"}), ("en-US", typeof(MolarEntropyUnit), (int)MolarEntropyUnit.MegajoulePerMoleKelvin, new string[]{"MJ/(mol*K)"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.CentimolesPerLiter, new string[]{"cmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.CentimolesPerLiter, new string[]{"cM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.DecimolesPerLiter, new string[]{"dmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.DecimolesPerLiter, new string[]{"dM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MicromolesPerLiter, new string[]{"µmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MicromolesPerLiter, new string[]{"µM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MillimolesPerLiter, new string[]{"mmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MillimolesPerLiter, new string[]{"mM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.CentimolesPerLiter, new string[]{"cmol/L", "cM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.DecimolesPerLiter, new string[]{"dmol/L", "dM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MicromolesPerLiter, new string[]{"µmol/L", "µM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MillimolesPerLiter, new string[]{"mmol/L", "mM"}), ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MolesPerCubicMeter, new string[]{"mol/m³"}), ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MolesPerLiter, new string[]{"mol/L", "M"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.NanomolesPerLiter, new string[]{"nmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.NanomolesPerLiter, new string[]{"nM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.PicomolesPerLiter, new string[]{"pmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.PicomolesPerLiter, new string[]{"pM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.NanomolesPerLiter, new string[]{"nmol/L", "nM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.PicomolesPerLiter, new string[]{"pmol/L", "pM"}), ("en-US", typeof(MolarMassUnit), (int)MolarMassUnit.CentigramPerMole, new string[]{"cg/mol"}), ("ru-RU", typeof(MolarMassUnit), (int)MolarMassUnit.CentigramPerMole, new string[]{"сг/моль"}), ("en-US", typeof(MolarMassUnit), (int)MolarMassUnit.DecagramPerMole, new string[]{"dag/mol"}), @@ -902,7 +856,6 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(PressureUnit), (int)PressureUnit.Kilopascal, new string[]{"кПа"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.KilopoundForcePerSquareFoot, new string[]{"kipf/ft²"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.KilopoundForcePerSquareInch, new string[]{"kipf/in²"}), - ("en-US", typeof(PressureUnit), (int)PressureUnit.KilopoundForcePerSquareInch, new string[]{"kipf/in²"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.Megabar, new string[]{"Mbar"}), ("ru-RU", typeof(PressureUnit), (int)PressureUnit.Megabar, new string[]{"Mбар"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.MeganewtonPerSquareMeter, new string[]{"MN/m²"}), @@ -974,18 +927,15 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.DegreePerMinute, new string[]{"°/min", "deg/min"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.DegreePerSecond, new string[]{"°/s", "deg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.DegreePerSecond, new string[]{"°/с"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µ°/s"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µdeg/s"}), + ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µ°/s", "µdeg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µ°/с"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicroradianPerSecond, new string[]{"µrad/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicroradianPerSecond, new string[]{"µрад/с"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"m°/s"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"mdeg/s"}), + ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"m°/s", "mdeg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"m°/с"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MilliradianPerSecond, new string[]{"mrad/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MilliradianPerSecond, new string[]{"mрад/с"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"n°/s"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"ndeg/s"}), + ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"n°/s", "ndeg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"n°/с"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanoradianPerSecond, new string[]{"nrad/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanoradianPerSecond, new string[]{"nрад/с"}), @@ -995,15 +945,11 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.RevolutionPerMinute, new string[]{"об/мин"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.RevolutionPerSecond, new string[]{"r/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.RevolutionPerSecond, new string[]{"об/с"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.KilonewtonMeterPerRadian, new string[]{"kN·m/rad"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.KilonewtonMeterPerRadian, new string[]{"kNm/rad"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.MeganewtonMeterPerRadian, new string[]{"MN·m/rad"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.MeganewtonMeterPerRadian, new string[]{"MNm/rad"}), + ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.KilonewtonMeterPerRadian, new string[]{"kN·m/rad", "kNm/rad"}), + ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.MeganewtonMeterPerRadian, new string[]{"MN·m/rad", "MNm/rad"}), ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.NewtonMeterPerRadian, new string[]{"N·m/rad", "Nm/rad"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.KilonewtonMeterPerRadianPerMeter, new string[]{"kN·m/rad/m"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.KilonewtonMeterPerRadianPerMeter, new string[]{"kNm/rad/m"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.MeganewtonMeterPerRadianPerMeter, new string[]{"MN·m/rad/m"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.MeganewtonMeterPerRadianPerMeter, new string[]{"MNm/rad/m"}), + ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.KilonewtonMeterPerRadianPerMeter, new string[]{"kN·m/rad/m", "kNm/rad/m"}), + ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.MeganewtonMeterPerRadianPerMeter, new string[]{"MN·m/rad/m", "MNm/rad/m"}), ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.NewtonMeterPerRadianPerMeter, new string[]{"N·m/rad/m", "Nm/rad/m"}), ("en-US", typeof(SolidAngleUnit), (int)SolidAngleUnit.Steradian, new string[]{"sr"}), ("en-US", typeof(SpecificEnergyUnit), (int)SpecificEnergyUnit.BtuPerPound, new string[]{"btu/lb"}), @@ -1247,9 +1193,7 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.AcreFootPerHour, new string[]{"af/h"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.AcreFootPerMinute, new string[]{"af/m"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.AcreFootPerSecond, new string[]{"af/s"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cl/day", "cL/d", "cLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerMinute, new string[]{"cLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicDecimeterPerMinute, new string[]{"dm³/min"}), ("ru-RU", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicDecimeterPerMinute, new string[]{"дм³/мин"}), @@ -1268,13 +1212,9 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicYardPerHour, new string[]{"yd³/h"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicYardPerMinute, new string[]{"yd³/min"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicYardPerSecond, new string[]{"yd³/s"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dl/day", "dL/d", "dLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerMinute, new string[]{"dLPM"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kl/day", "kL/d", "kLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerMinute, new string[]{"kLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KilousGallonPerMinute, new string[]{"kgal (U.S.)/min", "KGPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerDay, new string[]{"l/day", "L/d", "LPD"}), @@ -1282,22 +1222,14 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerHour, new string[]{"л/ч"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerMinute, new string[]{"LPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerSecond, new string[]{"LPS"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"Ml/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"ML/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"MLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"Ml/day", "ML/d", "MLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaukGallonPerSecond, new string[]{"Mgal (imp.)/s"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µl/day", "µL/d", "µLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerMinute, new string[]{"µLPM"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"ml/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"mL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"mLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"ml/day", "mL/d", "mLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerMinute, new string[]{"mLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MillionUsGallonsPerDay, new string[]{"MGD"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nl/day", "nL/d", "nLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerMinute, new string[]{"nLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.OilBarrelPerDay, new string[]{"bbl/d", "BOPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.OilBarrelPerHour, new string[]{"bbl/hr", "bph"}), diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/GenerateUnits.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/GenerateUnits.ps1 deleted file mode 100644 index 7147eeabc5..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/GenerateUnits.ps1 +++ /dev/null @@ -1,343 +0,0 @@ -using module ".\Types.psm1" - -#Requires -Version 5.1 - -# Set Write-Output used by Include- files to UTF8 encoding to fix copyright character -[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8 -$OutputEncoding = [Text.UTF8Encoding]::UTF8 - -# DaddyCool => daddyCool -function ToCamelCase($str) -{ - return $str.Substring(0,1).ToLowerInvariant() + $str.Substring(1) -} - -function ValueOrDefault($value, $defaultValue){ - if ($value -ne $null) { $value } else { $defaultValue } -} - -function GenerateQuantity([Quantity]$quantity, $outDir) -{ - $outFileName = "$outDir/$($quantity.Name).WindowsRuntimeComponent.g.cs" - GenerateQuantitySourceCode $quantity | Out-File -Encoding "UTF8" $outFileName | Out-Null - if (!$?) { exit 1 } - Write-Host -NoNewline "quantity(OK) " -} - -function GenerateUnitTestBaseClass([Quantity]$quantity, $outDir) -{ - $outFileName = "$outDir/$($quantity.Name)TestsBase.g.cs" - GenerateUnitTestBaseClassSourceCode $quantity | Out-File -Encoding "UTF8" $outFileName | Out-Null - if (!$?) { - exit 1 - } - Write-Host -NoNewline "test base(OK) " -} - -function GenerateUnitTestClassIfNotExists([Quantity]$quantity, $outDir) -{ - Write-Host -NoNewline "test stub" - $outFileName = "$outDir/$($quantity.Name)Tests.cs" - if (Test-Path $outFileName) - { - Write-Host -NoNewline "(skip) " - return - } - else - { - GenerateUnitTestPlaceholderSourceCode $quantity | Out-File -Encoding "UTF8" $outFileName | Out-Null - if (!$?) { - exit 1 - } - Write-Host -NoNewline "(OK) " - } -} - -function GenerateUnitType([Quantity]$quantity, $outDir) -{ - $outFileName = "$outDir/$($quantity.Name)Unit.g.cs" - - GenerateUnitTypeSourceCode $quantity | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - exit 1 - } - Write-Host -NoNewline "unit(OK) " -} - -function GenerateUnitSystemDefault($quantities, $outDir) -{ - Write-Host -NoNewline "UnitAbbreviationsCache.g.cs: " - $outFileName = "$outDir/UnitAbbreviationsCache.g.cs" - - GenerateUnitSystemDefaultSourceCode $quantities | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - Write-Host "(error) " - exit 1 - } - Write-Host "(OK) " -} - -function GenerateQuantityType($quantities, $outDir) -{ - Write-Host -NoNewline "QuantityType.g.cs: " - $outFileName = "$outDir/QuantityType.g.cs" - - GenerateQuantityTypeSourceCode $quantities | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - Write-Host "(error) " - exit 1 - } - Write-Host "(OK) " -} - -function GenerateStaticQuantity($quantities, $outDir) -{ - Write-Host -NoNewline "Quantity.g.cs: " - $outFileName = "$outDir/Quantity.g.cs" - - GenerateStaticQuantitySourceCode $quantities | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - Write-Host "(error) " - exit 1 - } - Write-Host "(OK) " -} - -function EnsureDirExists([String] $dirPath) { - New-Item -ItemType Directory -Force -Path $dirPath | Out-Null - if (!$?) { - exit 1 - } -} - -function Set-ConversionFunctions -{ - param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $quantity) - PROCESS { - foreach ($u in $quantity.Units) { - - # Use decimal for internal calculations if base type is not double, such as for long or int. - if ($quantity.BaseType -eq "decimal") { - $u.FromUnitToBaseFunc = $u.FromUnitToBaseFunc -replace "d", "m" - $u.FromBaseToUnitFunc = $u.FromBaseToUnitFunc -replace "d", "m" - } - - # Convert to/from double for other base types - $u.FromUnitToBaseFunc = "$($u.FromUnitToBaseFunc)" - $u.FromBaseToUnitFunc = "$($u.FromBaseToUnitFunc)" - } - return $quantity - } -} - -function Add-PrefixUnits { - param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $quantity) - PROCESS { - $prefixUnits = @() - - foreach ($unit in $quantity.Units) - { - foreach ($localization in $unit.Localization){ - if($localization.AbbreviationsWithPrefixes.Count -gt 0){ - if($unit.Prefixes.Count -ne $localization.AbbreviationsWithPrefixes.Count){ - Write-Error "The prefix count ($($unit.Prefixes.Count)) does not match the abbreviations with prefixes count ($($localization.AbbreviationsWithPrefixes.Count)) for $($quantity.Name).$($unit.SingularName)" -ErrorAction Stop - } - } - } - - $prefixIndex = 0 - foreach ($prefix in $unit.Prefixes) - { - $prefixInfo = switch ($prefix) - { - "Kilo" { "k", "1e3d"; break; } - "Hecto" { "h", "1e2d"; break; } - "Deca" { "da", "1e1d"; break; } - "Deci" { "d", "1e-1d"; break; } - "Centi" { "c", "1e-2d"; break; } - "Milli" { "m", "1e-3d"; break; } - "Micro" { "µ", "1e-6d"; break; } - "Nano" { "n", "1e-9d"; break; } - - # Optimization, move less frequently used prefixes to the end - "Pico" { "p", "1e-12d"; break; } - "Femto" { "f", "1e-15d"; break; } - "Atto" { "a", "1e-18d"; break; } - "Zepto" { "z", "1e-21d"; break; } - "Yocto" { "y", "1e-24d"; break; } - - "Yotta" { "Y", "1e24d"; break; } - "Zetta" { "Z", "1e21d"; break; } - "Exa" { "E", "1e18d"; break; } - "Peta" { "P", "1e15d"; break; } - "Tera" { "T", "1e12d"; break; } - "Giga" { "G", "1e9d"; break; } - "Mega" { "M", "1e6d"; break; } - - # Binary prefixes - "Kibi" { "Ki", "1024d"; break; } - "Mebi" { "Mi", "(1024d * 1024)"; break; } - "Gibi" { "Gi", "(1024d * 1024 * 1024)"; break; } - "Tebi" { "Ti", "(1024d * 1024 * 1024 * 1024)"; break; } - "Pebi" { "Pi", "(1024d * 1024 * 1024 * 1024 * 1024)"; break; } - "Exbi" { "Ei", "(1024d * 1024 * 1024 * 1024 * 1024 * 1024)"; break; } - } - - $prefixAbbreviation = $prefixInfo[0] - $prefixFactor = $prefixInfo[1] - - $prefixUnit = New-Object PsObject -Property @{ - SingularName=$prefix + $(ToCamelCase $unit.SingularName) - PluralName=$prefix + $(ToCamelCase $unit.PluralName) - FromUnitToBaseFunc="("+$unit.FromUnitToBaseFunc+") * $prefixFactor" - FromBaseToUnitFunc="("+$unit.FromBaseToUnitFunc+") / $prefixFactor" - - Localization=$unit.Localization | % { - foreach ($abbrSyno in $_.Abbreviations) { - $abbrev = $prefixAbbreviation + $abbrSyno - if ($_.AbbreviationsWithPrefixes) { - if($_.AbbreviationsWithPrefixes[$prefixIndex] -isnot [System.String]){ - foreach($synoWithPrefix in $_.AbbreviationsWithPrefixes[$prefixIndex]){ - New-Object PsObject -Property @{ - Culture=$_.Culture - Abbreviations= $synoWithPrefix - } - } - continue - } - else{ - $abbrev = $_.AbbreviationsWithPrefixes[$prefixIndex] - } - } - New-Object PsObject -Property @{ - Culture=$_.Culture - Abbreviations= $abbrev - } - } - } - } - - # Append prefix unit - $prefixUnits += $prefixUnit - $prefixIndex++; - } # foreach prefixes - } # foreach units - - $quantity.Units += $prefixUnits - return $quantity - } -} - -function Set-UnitsOrderedByName { - param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $quantity) - PROCESS { - $quantity.Units = ($quantity.Units | sort SingularName) - return $quantity - } -} - -function Add-InheritedUnits([Quantity]$quantity, $quantities) { - - foreach ($inheritFromQuantityName in $quantity.InheritUnitsFrom) { - $inheritFromQuantity = $quantities | Where { $_.Name -eq $inheritFromQuantityName } | Select -First 1 - $quantity.Units += $inheritFromQuantity.Units - - Write-Host -NoNewline "(inherit $inheritFromQuantityName) " - } -} - -# Load external generator functions with same name as file -. "$PSScriptRoot/Include-GenerateTemplates.ps1" -. "$PSScriptRoot/Include-GenerateUnitSystemDefaultSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateQuantityTypeSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateStaticQuantitySourceCode.ps1" -. "$PSScriptRoot/Include-GenerateQuantitySourceCode.ps1" -. "$PSScriptRoot/Include-GenerateUnitTypeSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateUnitTestBaseClassSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateUnitTestPlaceholderSourceCode.ps1" - -EnsureDirExists ($quantityDir = "$PSScriptRoot/../GeneratedCode/Quantities") -EnsureDirExists ($unitEnumDir = "$PSScriptRoot/../GeneratedCode/Units") -EnsureDirExists ($unitSystemDir = "$PSScriptRoot/../GeneratedCode") -EnsureDirExists ($testsDir = "$PSScriptRoot/../../UnitsNet.Tests/GeneratedCode") -EnsureDirExists ($testsCustomCodeDir = "$PSScriptRoot/../../UnitsNet.Tests/CustomCode") - -$templatesDir = "$PSScriptRoot/../../Common/UnitDefinitions" -$pad = 25 - -# Parse unit definitions from .json files -# TODO Find a way to automap from JSON into Quantity type -$quantities = Get-ChildItem -Path $templatesDir -filter "*.json" ` - | %{(Get-Content $_.FullName -Encoding "UTF8" | Out-String)} ` - | ConvertFrom-Json ` - | %{ - # $_ | fl | out-string | write-host -foreground blue - # New-Object -TypeName Quantity -Verbose -Property @{ - [Quantity]@{ - Name = $_.Name - XmlDocSummary = $_.XmlDoc - XmlDocRemarks = $_.XmlDocRemarks - BaseUnit = $_.BaseUnit - BaseType = ValueOrDefault $_.BaseType "double" - BaseDimensions = @{ - Length = ValueOrDefault $_.BaseDimensions.L 0 - Mass = ValueOrDefault $_.BaseDimensions.M 0 - Time = ValueOrDefault $_.BaseDimensions.T 0 - ElectricCurrent = ValueOrDefault $_.BaseDimensions.I 0 - Temperature = ValueOrDefault $_.BaseDimensions.Θ 0 - AmountOfSubstance = ValueOrDefault $_.BaseDimensions.N 0 - LuminousIntensity = ValueOrDefault $_.BaseDimensions.J 0 - } - GenerateArithmetic = ValueOrDefault $_.GenerateArithmetic $true - Logarithmic = ValueOrDefault $_.Logarithmic $false - LogarithmicScalingFactor = ValueOrDefault $_.LogarithmicScalingFactor 1 - Units = $_.Units | %{ - # $_ | fl | out-string | Write-Host -ForegroundColor blue - [Unit]@{ - SingularName = $_.SingularName - PluralName = $_.PluralName - XmlDocSummary = $_.XmlDocSummary - XmlDocRemarks = $_.XmlDocRemarks - FromUnitToBaseFunc = $_.FromUnitToBaseFunc - FromBaseToUnitFunc = $_.FromBaseToUnitFunc - Prefixes = [string[]](ValueOrDefault $_.Prefixes @()) - Localization = $_.Localization | %{ - # $_ | fl | out-string | Write-Host -ForegroundColor blue - [Localization]@{ - Culture = $_.Culture - Abbreviations = $_.Abbreviations - AbbreviationsWithPrefixes = $_.AbbreviationsWithPrefixes - } - } - } - } - } - } ` - | Add-PrefixUnits ` - | Set-ConversionFunctions ` - | Set-UnitsOrderedByName - -foreach ($quantity in $quantities) { - Write-Host -NoNewline "$($quantity.Name):".PadRight($pad) - - Add-InheritedUnits $quantity $quantities - - GenerateQuantity $quantity $quantityDir - GenerateUnitType $quantity $unitEnumDir - GenerateUnitTestBaseClass $quantity $testsDir - GenerateUnitTestClassIfNotExists $quantity $testsCustomCodeDir - - Write-Host "" -} - -Write-Host "" -GenerateUnitSystemDefault $quantities $unitSystemDir -GenerateQuantityType $quantities $unitSystemDir -GenerateStaticQuantity $quantities $unitSystemDir - -$unitCount = ($quantities | %{$_.Units.Count} | Measure -Sum).Sum - -Write-Host "`n`n" -Write-Host -Foreground Yellow "Summary: $unitCount units in $($quantities.Count) quantities".PadRight($pad) -Write-Host "`n`n" -exit 0 diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateQuantitySourceCode.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateQuantitySourceCode.ps1 deleted file mode 100644 index f4d5fdff40..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateQuantitySourceCode.ps1 +++ /dev/null @@ -1,727 +0,0 @@ -using module ".\Types.psm1" - -function GenerateQuantitySourceCode([Quantity]$quantity) -{ - $quantityName = $quantity.Name; - $units = $quantity.Units; - $valueType = $quantity.BaseType; - [Unit]$baseUnit = $units | where { $_.SingularName -eq $quantity.BaseUnit } | Select-Object -First 1 - $baseUnitSingularName = $baseUnit.SingularName - $unitEnumName = "$quantityName" + "Unit" - - $baseDimensions = $quantity.BaseDimensions; - $isDimensionless = $null -eq $baseDimensions -or ( $baseDimensions.Length -eq 0 -and $baseDimensions.Mass -eq 0 -and $baseDimensions.Time -eq 0 -and $baseDimensions.ElectricCurrent -eq 0 -and $baseDimensions.Temperature -eq 0 -and $baseDimensions.AmountOfSubstance -eq 0 -and $baseDimensions.LuminousIntensity -eq 0 ) - - [GeneratorArgs]$genArgs = New-Object GeneratorArgs -Property @{ - Quantity = $quantity; - BaseUnit = $baseUnit; - UnitEnumName = $unitEnumName; - } - # $genArgs | fl | out-string | write-host -foreground yellow -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using System.Globalization; -using System.Linq; -using JetBrains.Annotations; -using UnitsNet.Units; -using UnitsNet.InternalHelpers; - -// ReSharper disable once CheckNamespace - -namespace UnitsNet -{ -"@; - $obsoleteAttribute = GetObsoleteAttribute($quantity); - if ($obsoleteAttribute) - { - $obsoleteAttribute = "`r`n " + $obsoleteAttribute; # apply padding to conformance with code format in this section - }@" - /// - /// $($quantity.XmlDocSummary) - /// -"@; if ($quantity.XmlDocRemarks) {@" - /// - /// $($quantity.XmlDocRemarks) - /// -"@; }@" - // Windows Runtime Component has constraints on public types: https://msdn.microsoft.com/en-us/library/br230301.aspx#Declaring types in Windows Runtime Components - // Public structures can't have any members other than public fields, and those fields must be value types or strings. - // Public classes must be sealed (NotInheritable in Visual Basic). If your programming model requires polymorphism, you can create a public interface and implement that interface on the classes that must be polymorphic. - public sealed partial class $quantityName : IQuantity - { - /// - /// The numeric value this quantity was constructed with. - /// - private readonly $valueType _value; - - /// - /// The unit this quantity was constructed with. - /// - private readonly $($unitEnumName)? _unit; - - static $quantityName() - { -"@; if($isDimensionless) {@" - BaseDimensions = BaseDimensions.Dimensionless; -"@; } else {@" - BaseDimensions = new BaseDimensions($($baseDimensions.Length), $($baseDimensions.Mass), $($baseDimensions.Time), $($baseDimensions.ElectricCurrent), $($baseDimensions.Temperature), $($baseDimensions.AmountOfSubstance), $($baseDimensions.LuminousIntensity)); -"@; }@" - Info = new QuantityInfo(QuantityType.$quantityName, Units.Cast().ToArray(), BaseUnit, Zero, BaseDimensions); - } - - /// - /// Creates the quantity with a value of 0 in the base unit $baseUnitSingularName. - /// - /// - /// Windows Runtime Component requires a default constructor. - /// - public $quantityName() - { - _value = 0; - _unit = BaseUnit; - } - - /// - /// Creates the quantity with the given numeric value and unit. - /// - /// The numeric value to contruct this quantity with. - /// The unit representation to contruct this quantity with. - /// Value parameter cannot be named 'value' due to constraint when targeting Windows Runtime Component. - /// If value is NaN or Infinity. - private $quantityName($valueType numericValue, $unitEnumName unit) - { - if(unit == $unitEnumName.Undefined) - throw new ArgumentException("The quantity can not be created with an undefined unit.", nameof(unit)); - -"@; if ($quantity.BaseType -eq "double") {@" - _value = Guard.EnsureValidNumber(numericValue, nameof(numericValue)); -"@; } else {@" - _value = numericValue; -"@; }@" - _unit = unit; - } -"@; - GenerateStaticProperties $genArgs - GenerateProperties $genArgs - GenerateConversionProperties $genArgs - GenerateStaticMethods $genArgs - GenerateStaticFactoryMethods $genArgs - GenerateStaticParseMethods $genArgs - GenerateEqualityAndComparison $genArgs - GenerateConversionMethods $genArgs -@" - - #region ToString Methods - - /// - /// Get default string representation of value and unit. - /// - /// String representation. - public override string ToString() - { - return ToString(null); - } - - /// - /// Get string representation of value and unit. Using two significant digits after radix. - /// - /// String representation. - /// Name of culture (ex: "en-US") to use for localization and number formatting. Defaults to if null. - public string ToString([CanBeNull] string cultureName) - { - var provider = cultureName; - return ToString(provider, 2); - } - - /// - /// Get string representation of value and unit. - /// - /// The number of significant digits after the radix point. - /// String representation. - /// Name of culture (ex: "en-US") to use for localization and number formatting. Defaults to if null. - public string ToString(string cultureName, int significantDigitsAfterRadix) - { - var provider = cultureName; - var value = Convert.ToDouble(Value); - var format = UnitFormatter.GetFormat(value, significantDigitsAfterRadix); - return ToString(provider, format); - } - - /// - /// Get string representation of value and unit. - /// - /// String format to use. Default: "{0:0.##} {1} for value and unit abbreviation respectively." - /// Arguments for string format. Value and unit are implictly included as arguments 0 and 1. - /// String representation. - /// Name of culture (ex: "en-US") to use for localization and number formatting. Defaults to if null. - public string ToString([CanBeNull] string cultureName, [NotNull] string format, [NotNull] params object[] args) - { - var provider = GetFormatProviderFromCultureName(cultureName); - if (format == null) throw new ArgumentNullException(nameof(format)); - if (args == null) throw new ArgumentNullException(nameof(args)); - - provider = provider ?? GlobalConfiguration.DefaultCulture; - - var value = Convert.ToDouble(Value); - var formatArgs = UnitFormatter.GetFormatArgs(Unit, value, provider, args); - return string.Format(provider, format, formatArgs); - } - - #endregion - - private static IFormatProvider GetFormatProviderFromCultureName([CanBeNull] string cultureName) - { - return cultureName != null ? new CultureInfo(cultureName) : (IFormatProvider)null; - } - } -} -"@; -} - -function GenerateStaticProperties([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $baseUnitSingularName = $genArgs.BaseUnit.SingularName - $valueType = $genArgs.Quantity.BaseType -@" - - #region Static Properties - - /// - /// Information about the quantity type, such as unit values and names. - /// - internal static QuantityInfo Info { get; } - - /// - /// The of this quantity. - /// - public static BaseDimensions BaseDimensions { get; } - - /// - /// The base unit of $quantityName, which is $baseUnitSingularName. All conversions go via this value. - /// - public static $unitEnumName BaseUnit { get; } = $unitEnumName.$baseUnitSingularName; - - /// - /// Represents the largest possible value of $quantityName - /// - public static $quantityName MaxValue { get; } = new $quantityName($valueType.MaxValue, BaseUnit); - - /// - /// Represents the smallest possible value of $quantityName - /// - public static $quantityName MinValue { get; } = new $quantityName($valueType.MinValue, BaseUnit); - - /// - /// The of this quantity. - /// - public static QuantityType QuantityType { get; } = QuantityType.$quantityName; - - /// - /// All units of measurement for the $quantityName quantity. - /// - public static $unitEnumName[] Units { get; } = Enum.GetValues(typeof($unitEnumName)).Cast<$unitEnumName>().Except(new $unitEnumName[]{ $unitEnumName.Undefined }).ToArray(); - - /// - /// Gets an instance of this quantity with a value of 0 in the base unit $baseUnitSingularName. - /// - public static $quantityName Zero { get; } = new $quantityName(0, BaseUnit); - - #endregion -"@; -} - -function GenerateProperties([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName -@" - - #region Properties - - /// - /// The numeric value this quantity was constructed with. - /// - public double Value => Convert.ToDouble(_value); - - /// - object IQuantity.Unit => Unit; - - /// - /// The unit this quantity was constructed with -or- if default ctor was used. - /// - public $unitEnumName Unit => _unit.GetValueOrDefault(BaseUnit); - - internal QuantityInfo QuantityInfo => Info; - - /// - /// The of this quantity. - /// - public QuantityType Type => $quantityName.QuantityType; - - /// - /// The of this quantity. - /// - public BaseDimensions Dimensions => $quantityName.BaseDimensions; - - #endregion -"@; -} - -function GenerateConversionProperties([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $units = $genArgs.Quantity.Units -@" - - #region Conversion Properties -"@; - foreach ($unit in $units) { - $propertyName = $unit.PluralName; - $obsoleteAttribute = GetObsoleteAttribute($unit); - if ($obsoleteAttribute) - { - $obsoleteAttribute = "`r`n " + $obsoleteAttribute; # apply padding to conformance with code format in this page - } -@" - - /// - /// Get $quantityName in $propertyName. - /// $($obsoleteAttribute) - public double $propertyName => As($unitEnumName.$($unit.SingularName)); -"@; } -@" - - #endregion -"@; -} - -function GenerateStaticMethods([GeneratorArgs]$genArgs) -{ - $unitEnumName = $genArgs.UnitEnumName -@" - - #region Static Methods - - /// - /// Get unit abbreviation string. - /// - /// Unit to get abbreviation for. - /// Unit abbreviation string. - public static string GetAbbreviation($unitEnumName unit) - { - return GetAbbreviation(unit, null); - } - - /// - /// Get unit abbreviation string. - /// - /// Unit to get abbreviation for. - /// Unit abbreviation string. - /// Name of culture (ex: "en-US") to use when parsing number and unit. Defaults to if null. - public static string GetAbbreviation($unitEnumName unit, [CanBeNull] string cultureName) - { - IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); - return UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit, provider); - } - - #endregion -"@; -} - -function GenerateStaticFactoryMethods([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $units = $genArgs.Quantity.Units - $valueType = $genArgs.Quantity.BaseType -@" - - #region Static Factory Methods - -"@; foreach ($unit in $units) { - $valueParamName = $unit.PluralName.ToLowerInvariant(); - $obsoleteAttribute = GetObsoleteAttribute($unit); - if ($obsoleteAttribute) - { - $obsoleteAttribute = "`r`n " + $obsoleteAttribute; # apply padding to conformance with code format in this page - } - @" - /// - /// Get $quantityName from $($unit.PluralName). - /// $($obsoleteAttribute) - /// If value is NaN or Infinity. - [Windows.Foundation.Metadata.DefaultOverload] - public static $quantityName From$($unit.PluralName)(double $valueParamName) - { - $valueType value = ($valueType) $valueParamName; - return new $quantityName(value, $unitEnumName.$($unit.SingularName)); - } -"@; }@" - - /// - /// Dynamically convert from value and unit enum to . - /// - /// Value to convert from. - /// Unit to convert from. - /// $quantityName unit value. - // Fix name conflict with parameter "value" - [return: System.Runtime.InteropServices.WindowsRuntime.ReturnValueName("returnValue")] - public static $quantityName From(double value, $unitEnumName fromUnit) - { - return new $quantityName(($valueType)value, fromUnit); - } - - #endregion -"@; - -} - -function GenerateStaticParseMethods([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName -@" - - #region Static Parse Methods - - /// - /// Parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// - /// Expected string to have one or two pairs of quantity and unit in the format - /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" - /// - /// - /// More than one unit is represented by the specified unit abbreviation. - /// Example: Volume.Parse("1 cup") will throw, because it can refer to any of - /// , and . - /// - /// - /// If anything else goes wrong, typically due to a bug or unhandled case. - /// We wrap exceptions in to allow you to distinguish - /// Units.NET exceptions from other exceptions. - /// - public static $quantityName Parse(string str) - { - return Parse(str, null); - } - - /// - /// Parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// - /// Expected string to have one or two pairs of quantity and unit in the format - /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" - /// - /// - /// More than one unit is represented by the specified unit abbreviation. - /// Example: Volume.Parse("1 cup") will throw, because it can refer to any of - /// , and . - /// - /// - /// If anything else goes wrong, typically due to a bug or unhandled case. - /// We wrap exceptions in to allow you to distinguish - /// Units.NET exceptions from other exceptions. - /// - /// Name of culture (ex: "en-US") to use when parsing number and unit. Defaults to if null. - public static $quantityName Parse(string str, [CanBeNull] string cultureName) - { - IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); - return QuantityParser.Default.Parse<$quantityName, $unitEnumName>( - str, - provider, - From); - } - - /// - /// Try to parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// Resulting unit quantity if successful. - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - public static bool TryParse([CanBeNull] string str, out $quantityName result) - { - return TryParse(str, null, out result); - } - - /// - /// Try to parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// Resulting unit quantity if successful. - /// True if successful, otherwise false. - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - /// Name of culture (ex: "en-US") to use when parsing number and unit. Defaults to if null. - public static bool TryParse([CanBeNull] string str, [CanBeNull] string cultureName, out $quantityName result) - { - IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); - return QuantityParser.Default.TryParse<$quantityName, $unitEnumName>( - str, - provider, - From, - out result); - } - - /// - /// Parse a unit string. - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.ParseUnit("m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// Error parsing string. - public static $unitEnumName ParseUnit(string str) - { - return ParseUnit(str, null); - } - - /// - /// Parse a unit string. - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.ParseUnit("m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// Error parsing string. - /// Name of culture (ex: "en-US") to use when parsing number and unit. Defaults to if null. - public static $unitEnumName ParseUnit(string str, [CanBeNull] string cultureName) - { - IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); - return UnitParser.Default.Parse<$unitEnumName>(str, provider); - } - - public static bool TryParseUnit(string str, out $unitEnumName unit) - { - return TryParseUnit(str, null, out unit); - } - - /// - /// Parse a unit string. - /// - /// String to parse. Typically in the form: {number} {unit} - /// The parsed unit if successful. - /// True if successful, otherwise false. - /// - /// Length.TryParseUnit("m", new CultureInfo("en-US")); - /// - /// Name of culture (ex: "en-US") to use when parsing number and unit. Defaults to if null. - public static bool TryParseUnit(string str, [CanBeNull] string cultureName, out $unitEnumName unit) - { - IFormatProvider provider = GetFormatProviderFromCultureName(cultureName); - return UnitParser.Default.TryParse<$unitEnumName>(str, provider, out unit); - } - - #endregion -"@; -} - -function GenerateEqualityAndComparison([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name -@" - - #region Equality / IComparable - - public int CompareTo(object obj) - { - if(obj is null) throw new ArgumentNullException(nameof(obj)); - if(!(obj is $quantityName obj$quantityName)) throw new ArgumentException("Expected type $quantityName.", nameof(obj)); - - return CompareTo(obj$quantityName); - } - - // 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 - internal int CompareTo($quantityName other) - { - return _value.CompareTo(other.AsBaseNumericType(this.Unit)); - } - - [Windows.Foundation.Metadata.DefaultOverload] - public override bool Equals(object obj) - { - if(obj is null || !(obj is $quantityName obj$quantityName)) - return false; - - return Equals(obj$quantityName); - } - - public bool Equals($quantityName other) - { - return _value.Equals(other.AsBaseNumericType(this.Unit)); - } - - /// - /// - /// Compare equality to another $quantityName within the given absolute or relative tolerance. - /// - /// - /// Relative tolerance is defined as the maximum allowable absolute difference between this quantity's value and - /// as a percentage of this quantity's value. will be converted into - /// this quantity's unit for comparison. A relative tolerance of 0.01 means the absolute difference must be within +/- 1% of - /// this quantity's value to be considered equal. - /// - /// In this example, the two quantities will be equal if the value of b is within +/- 1% of a (0.02m or 2cm). - /// - /// var a = Length.FromMeters(2.0); - /// var b = Length.FromInches(50.0); - /// a.Equals(b, 0.01, ComparisonType.Relative); - /// - /// - /// - /// - /// Absolute tolerance is defined as the maximum allowable absolute difference between this quantity's value and - /// as a fixed number in this quantity's unit. will be converted into - /// this quantity's unit for comparison. - /// - /// In this example, the two quantities will be equal if the value of b is within 0.01 of a (0.01m or 1cm). - /// - /// var a = Length.FromMeters(2.0); - /// var b = Length.FromInches(50.0); - /// a.Equals(b, 0.01, ComparisonType.Absolute); - /// - /// - /// - /// - /// Note that it is advised against specifying zero difference, due to the nature - /// of floating point operations and using System.Double internally. - /// - /// - /// The other quantity to compare to. - /// The absolute or relative tolerance value. Must be greater than or equal to 0. - /// The comparison type: either relative or absolute. - /// True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance. - public bool Equals($quantityName other, double tolerance, ComparisonType comparisonType) - { - if(tolerance < 0) - throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0."); - - double thisValue = (double)this.Value; - double otherValueInThisUnits = other.As(this.Unit); - - return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType); - } - - /// - /// Returns the hash code for this instance. - /// - /// A hash code for the current $quantityName. - public override int GetHashCode() - { - return new { QuantityType, Value, Unit }.GetHashCode(); - } - - #endregion -"@; -} - -function GenerateConversionMethods([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $valueType = $genArgs.Quantity.BaseType -@" - - #region Conversion Methods - - double IQuantity.As(object unit) => As(($unitEnumName)unit); - - /// - /// Convert to the unit representation . - /// - /// Value converted to the specified unit. - public double As($unitEnumName unit) - { - if(Unit == unit) - return Convert.ToDouble(Value); - - var converted = AsBaseNumericType(unit); - return Convert.ToDouble(converted); - } - - /// - /// Converts this $quantityName to another $quantityName with the unit representation . - /// - /// A $quantityName with the specified unit. - public $quantityName ToUnit($unitEnumName unit) - { - var convertedValue = AsBaseNumericType(unit); - return new $quantityName(convertedValue, unit); - } - - /// - /// Converts the current value + unit to the base unit. - /// This is typically the first step in converting from one unit to another. - /// - /// The value in the base unit representation. - private $valueType AsBaseUnit() - { - switch(Unit) - { -"@; foreach ($unit in $units) { - $func = $unit.FromUnitToBaseFunc.Replace("x", "_value");@" - case $unitEnumName.$($unit.SingularName): return $func; -"@; }@" - default: - throw new NotImplementedException($"Can not convert {Unit} to base units."); - } - } - - private $valueType AsBaseNumericType($unitEnumName unit) - { - if(Unit == unit) - return _value; - - var baseUnitValue = AsBaseUnit(); - - switch(unit) - { -"@; foreach ($unit in $units) { - $func = $unit.FromBaseToUnitFunc.Replace("x", "baseUnitValue");@" - case $unitEnumName.$($unit.SingularName): return $func; -"@; }@" - default: - throw new NotImplementedException($"Can not convert {Unit} to {unit}."); - } - } - - #endregion -"@; -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateStaticQuantitySourceCode.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateStaticQuantitySourceCode.ps1 deleted file mode 100644 index 34b6eedd92..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateStaticQuantitySourceCode.ps1 +++ /dev/null @@ -1,123 +0,0 @@ -using module ".\Types.psm1" - -function GenerateStaticQuantitySourceCode([Quantity[]]$quantities, [string]$target) -{ -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using System.Linq; -using JetBrains.Annotations; -using UnitsNet.InternalHelpers; -using UnitsNet.Units; - -namespace UnitsNet -{ - /// - /// Dynamically parse or construct quantities when types are only known at runtime. - /// - internal static partial class Quantity - { - /// - /// Try to dynamically construct a quantity. - /// - /// Numeric value. - /// Unit enum value. - /// The resulting quantity if successful, otherwise default. - /// True if successful with assigned the value, otherwise false. - internal static bool TryFrom(double value, Enum unit, out IQuantity quantity) - { - switch (unit) - { -"@; foreach ($quantity in $quantities) { - $quantityName = $quantity.Name - $unitTypeName = $quantityName + "Unit" - $unitValue = toCamelCase($unitTypeName);@" - case $unitTypeName $($unitValue): - quantity = $quantityName.From(value, $unitValue); - return true; -"@; }@" - default: - { - quantity = default(IQuantity); - return false; - } - } - } - - /// - internal static IQuantity Parse(Type quantityType, string quantityString) => Parse(null, quantityType, quantityString); - - /// - /// Dynamically parse a quantity string representation. - /// - /// The format provider to use for lookup. Defaults to if null. - /// Type of quantity, such as . - /// Quantity string representation, such as "1.5 kg". Must be compatible with given quantity type. - /// The parsed quantity. - /// Type must be of type UnitsNet.IQuantity -or- Type is not a known quantity type. - internal static IQuantity Parse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString) - { - if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType)) - throw new ArgumentException($"Type {quantityType} must be of type UnitsNet.IQuantity."); - - if (TryParse(formatProvider, quantityType, quantityString, out IQuantity quantity)) return quantity; - - throw new ArgumentException($"Quantity string could not be parsed to quantity {quantityType}."); - } - - /// - internal static bool TryParse(Type quantityType, string quantityString, out IQuantity quantity) => - TryParse(null, quantityType, quantityString, out quantity); - - /// - /// Try to dynamically parse a quantity string representation. - /// - /// The format provider to use for lookup. Defaults to if null. - /// Type of quantity, such as . - /// Quantity string representation, such as "1.5 kg". Must be compatible with given quantity type. - /// The resulting quantity if successful, otherwise default. - /// The parsed quantity. - internal static bool TryParse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString, out IQuantity quantity) - { - quantity = default(IQuantity); - - if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType)) - return false; - - var parser = QuantityParser.Default; - -"@; foreach ($quantity in $quantities) { - $quantityName = $quantity.Name;@" - if (quantityType == typeof($quantityName)) - return parser.TryParse<$quantityName, $($quantityName)Unit>(quantityString, formatProvider, $quantityName.From, out quantity); - -"@; }@" - throw new ArgumentException( - $"Type {quantityType} is not a known quantity type. Did you pass in a third-party quantity type defined outside UnitsNet library?"); - } - } -} -"@; -} - -function toCamelCase([string] $str) { - return [char]::ToLower($str[0]) + $str.Substring(1) -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateTemplates.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateTemplates.ps1 deleted file mode 100644 index f6b2d15ce7..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateTemplates.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -<# -.SYNOPSIS -Returns the Obsolete attribute if ObsoleteText has been defined on the JSON input - otherwise returns empty string -It is up to the consumer to wrap any padding/new lines in order to keep to correct indentation formats -#> -function GetObsoleteAttribute($quantityOrUnit) -{ - if ($quantityOrUnit.ObsoleteText) - { - return "[System.Obsolete(""$($quantityOrUnit.ObsoleteText)"")]"; - } - return ""; -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitSystemDefaultSourceCode.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitSystemDefaultSourceCode.ps1 deleted file mode 100644 index 5220b72310..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitSystemDefaultSourceCode.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -function GenerateUnitSystemDefaultSourceCode($quantities) -{ -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using UnitsNet.Units; - -// ReSharper disable RedundantCommaInArrayInitializer -// ReSharper disable once CheckNamespace -namespace UnitsNet -{ - public sealed partial class UnitAbbreviationsCache - { - private static readonly (string CultureName, Type UnitType, int UnitValue, string[] UnitAbbreviations)[] GeneratedLocalizations - = new [] - { -"@; - foreach ($quantity in $quantities) - { - $quantityName = $quantity.Name; - $unitEnumName = "$quantityName" + "Unit"; - - foreach ($unit in $quantity.Units) - { - $enumValue = $unit.SingularName; - - foreach ($localization in $unit.Localization) - { - $cultureName = $localization.Culture; - $abbreviationParams = $localization.Abbreviations -join '", "' -@" - (`"$cultureName`", typeof($unitEnumName), (int)$unitEnumName.$enumValue, new string[]{`"$abbreviationParams`"}), -"@; - } - } - } -@" - }; - } -} -"@; -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTestBaseClassSourceCode.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTestBaseClassSourceCode.ps1 deleted file mode 100644 index 71a80f1db7..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTestBaseClassSourceCode.ps1 +++ /dev/null @@ -1,300 +0,0 @@ -using module ".\Types.psm1" -function GenerateUnitTestBaseClassSourceCode([Quantity]$quantity) -{ - $quantityName = $quantity.Name - $units = $quantity.Units - $valueType = $quantity.BaseType - [Unit]$baseUnit = $units | where { $_.SingularName -eq $quantity.BaseUnit } | Select-Object -First 1 - $baseUnitSingularName = $baseUnit.SingularName - $baseUnitPluralName = $baseUnit.PluralName - $baseUnitVariableName = $baseUnitSingularName.ToLowerInvariant() - $unitEnumName = "$quantityName" + "Unit" - -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using System.Linq; -using UnitsNet.Units; -using Xunit; - -// Disable build warning CS1718: Comparison made to same variable; did you mean to compare something else? -#pragma warning disable 1718 - -// ReSharper disable once CheckNamespace -namespace UnitsNet.Tests -{ - /// - /// Test of $quantityName. - /// -// ReSharper disable once PartialTypeWithSinglePart - public abstract partial class $($quantityName)TestsBase - { -"@; foreach ($unit in $units) {@" - protected abstract double $($unit.PluralName)InOne$($baseUnit.SingularName) { get; } -"@; }@" - -// ReSharper disable VirtualMemberNeverOverriden.Global -"@; foreach ($unit in $units) {@" - protected virtual double $($unit.PluralName)Tolerance { get { return 1e-5; } } -"@; }@" -// ReSharper restore VirtualMemberNeverOverriden.Global - - [Fact] - public void Ctor_WithUndefinedUnit_ThrowsArgumentException() - { - Assert.Throws(() => new $quantityName(($valueType)0.0, $unitEnumName.Undefined)); - } - -"@; if ($quantity.BaseType -eq "double") {@" - [Fact] - public void Ctor_WithInfinityValue_ThrowsArgumentException() - { - Assert.Throws(() => new $quantityName(double.PositiveInfinity, $unitEnumName.$($baseUnit.SingularName))); - Assert.Throws(() => new $quantityName(double.NegativeInfinity, $unitEnumName.$($baseUnit.SingularName))); - } - - [Fact] - public void Ctor_WithNaNValue_ThrowsArgumentException() - { - Assert.Throws(() => new $quantityName(double.NaN, $unitEnumName.$($baseUnit.SingularName))); - } -"@; }@" - - [Fact] - public void $($baseUnit.SingularName)To$($quantityName)Units() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance($($unit.PluralName)InOne$($baseUnit.SingularName), $baseUnitVariableName.$($unit.PluralName), $($unit.PluralName)Tolerance); -"@; }@" - } - - [Fact] - public void FromValueAndUnit() - { -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance(1, $quantityName.From(1, $unitEnumName.$($unit.SingularName)).$($unit.PluralName), $($unit.PluralName)Tolerance); -"@; }@" - } - -"@; if ($quantity.BaseType -eq "double") {@" - [Fact] - public void From$($baseUnit.PluralName)_WithInfinityValue_ThrowsArgumentException() - { - Assert.Throws(() => $quantityName.From$($baseUnit.PluralName)(double.PositiveInfinity)); - Assert.Throws(() => $quantityName.From$($baseUnit.PluralName)(double.NegativeInfinity)); - } - - [Fact] - public void From$($baseUnit.PluralName)_WithNanValue_ThrowsArgumentException() - { - Assert.Throws(() => $quantityName.From$($baseUnit.PluralName)(double.NaN)); - } -"@; }@" - - [Fact] - public void As() - { - var $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance($($unit.PluralName)InOne$($baseUnit.SingularName), $baseUnitVariableName.As($($quantityName)Unit.$($unit.SingularName)), $($unit.PluralName)Tolerance); -"@; }@" - } - - [Fact] - public void ToUnit() - { - var $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) -{ - $asQuantityVariableName = "$($unit.SingularName.ToLowerInvariant())Quantity"; -@" - - var $asQuantityVariableName = $baseUnitVariableName.ToUnit($($quantityName)Unit.$($unit.SingularName)); - AssertEx.EqualTolerance($($unit.PluralName)InOne$($baseUnit.SingularName), (double)$asQuantityVariableName.Value, $($unit.PluralName)Tolerance); - Assert.Equal($($quantityName)Unit.$($unit.SingularName), $asQuantityVariableName.Unit); -"@; }@" - } - - [Fact] - public void ConversionRoundTrip() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance(1, $quantityName.From$($unit.PluralName)($baseUnitVariableName.$($unit.PluralName)).$baseUnitPluralName, $($unit.PluralName)Tolerance); -"@; }@" - } - -"@; if ($quantity.Logarithmic -eq $true) {@" - [Fact] - public void LogarithmicArithmeticOperators() - { - $quantityName v = $quantityName.From$baseUnitPluralName(40); - AssertEx.EqualTolerance(-40, -v.$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertLogarithmicAddition(); - AssertLogarithmicSubtraction(); - AssertEx.EqualTolerance(50, (v*10).$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertEx.EqualTolerance(50, (10*v).$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertEx.EqualTolerance(35, (v/5).$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertEx.EqualTolerance(35, v/$quantityName.From$baseUnitPluralName(5), $($unit.PluralName)Tolerance); - } - - protected abstract void AssertLogarithmicAddition(); - - protected abstract void AssertLogarithmicSubtraction(); - -"@; } - elseif ($quantity.GenerateArithmetic -eq $true) {@" - [Fact] - public void ArithmeticOperators() - { - $quantityName v = $quantityName.From$baseUnitPluralName(1); - AssertEx.EqualTolerance(-1, -v.$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, ($quantityName.From$baseUnitPluralName(3)-v).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, (v + v).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(10, (v*10).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(10, (10*v).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, ($quantityName.From$baseUnitPluralName(10)/5).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, $quantityName.From$baseUnitPluralName(10)/$quantityName.From$baseUnitPluralName(5), $($baseUnit.PluralName)Tolerance); - } -"@; }@" - - [Fact] - public void ComparisonOperators() - { - $quantityName one$($baseUnit.SingularName) = $quantityName.From$baseUnitPluralName(1); - $quantityName two$baseUnitPluralName = $quantityName.From$baseUnitPluralName(2); - - Assert.True(one$($baseUnit.SingularName) < two$baseUnitPluralName); - Assert.True(one$($baseUnit.SingularName) <= two$baseUnitPluralName); - Assert.True(two$baseUnitPluralName > one$($baseUnit.SingularName)); - Assert.True(two$baseUnitPluralName >= one$($baseUnit.SingularName)); - - Assert.False(one$($baseUnit.SingularName) > two$baseUnitPluralName); - Assert.False(one$($baseUnit.SingularName) >= two$baseUnitPluralName); - Assert.False(two$baseUnitPluralName < one$($baseUnit.SingularName)); - Assert.False(two$baseUnitPluralName <= one$($baseUnit.SingularName)); - } - - [Fact] - public void CompareToIsImplemented() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.Equal(0, $baseUnitVariableName.CompareTo($baseUnitVariableName)); - Assert.True($baseUnitVariableName.CompareTo($quantityName.Zero) > 0); - Assert.True($quantityName.Zero.CompareTo($baseUnitVariableName) < 0); - } - - [Fact] - public void CompareToThrowsOnTypeMismatch() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.Throws(() => $baseUnitVariableName.CompareTo(new object())); - } - - [Fact] - public void CompareToThrowsOnNull() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.Throws(() => $baseUnitVariableName.CompareTo(null)); - } - - [Fact] - public void EqualityOperators() - { - var a = $quantityName.From$baseUnitPluralName(1); - var b = $quantityName.From$baseUnitPluralName(2); - - // ReSharper disable EqualExpressionComparison - - Assert.True(a == a); - Assert.False(a != a); - - Assert.True(a != b); - Assert.False(a == b); - - Assert.False(a == null); - Assert.False(null == a); - -// ReSharper restore EqualExpressionComparison - } - - [Fact] - public void EqualsIsImplemented() - { - var a = $quantityName.From$baseUnitPluralName(1); - var b = $quantityName.From$baseUnitPluralName(2); - - Assert.True(a.Equals(a)); - Assert.False(a.Equals(b)); - Assert.False(a.Equals(null)); - } - - [Fact] - public void EqualsRelativeToleranceIsImplemented() - { - var v = $quantityName.From$baseUnitPluralName(1); - Assert.True(v.Equals($quantityName.From$baseUnitPluralName(1), $($baseUnitPluralName)Tolerance, ComparisonType.Relative)); - Assert.False(v.Equals($quantityName.Zero, $($baseUnitPluralName)Tolerance, ComparisonType.Relative)); - } - - [Fact] - public void EqualsReturnsFalseOnTypeMismatch() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.False($baseUnitVariableName.Equals(new object())); - } - - [Fact] - public void EqualsReturnsFalseOnNull() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.False($baseUnitVariableName.Equals(null)); - } - - [Fact] - public void UnitsDoesNotContainUndefined() - { - Assert.DoesNotContain($unitEnumName.Undefined, $quantityName.Units); - } - - [Fact] - public void HasAtLeastOneAbbreviationSpecified() - { - var units = Enum.GetValues(typeof($unitEnumName)).Cast<$unitEnumName>(); - foreach(var unit in units) - { - if(unit == $unitEnumName.Undefined) - continue; - - var defaultAbbreviation = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit); - } - } - - [Fact] - public void BaseDimensionsShouldNeverBeNull() - { - Assert.False($quantityName.BaseDimensions is null); - } - } -} -"@; -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTestPlaceholderSourceCode.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTestPlaceholderSourceCode.ps1 deleted file mode 100644 index 055b2bfc8e..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTestPlaceholderSourceCode.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -function GenerateUnitTestPlaceholderSourceCode($quantity) -{ - $quantityName = $quantity.Name; -@" -//------------------------------------------------------------------------------ -// -// This code was generated (once) by \generate-code.bat, but will not be -// regenerated when it already exists. The purpose of creating this file is to make -// it easier to remember to implement all the unit conversion test cases. -// -// Whenever a new unit is added to this quantity and \generate-code.bat is run, -// the base test class will get a new abstract property and cause a compile error -// in this derived class, reminding the developer to implement the test case -// for the new unit. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; - -namespace UnitsNet.Tests.CustomCode -{ - public class $($quantityName)Tests : $($quantityName)TestsBase - { - // Override properties in base class here - } -} -"@; -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTypeSourceCode.ps1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTypeSourceCode.ps1 deleted file mode 100644 index 047f218560..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Include-GenerateUnitTypeSourceCode.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -function GenerateUnitTypeSourceCode($quantity) { - $quantityName = $quantity.Name; - $units = $quantity.Units; - $unitEnumName = "$($quantityName)Unit"; -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -// ReSharper disable once CheckNamespace -namespace UnitsNet.Units -{ - // Disable missing XML comment warnings for the generated unit enums. - #pragma warning disable 1591 - - public enum $unitEnumName - { - Undefined = 0, -"@; foreach ($unit in $units) { - $obsoleteAttribute = GetObsoleteAttribute($unit); - - if ($unit.XmlDocSummary) {@" - - /// - /// $($unit.XmlDocSummary) - /// -"@; } - if ($unit.XmlDocRemarks) {@" - /// $($unit.XmlDocRemarks) -"@; } - if ($obsoleteAttribute) {@" - $($obsoleteAttribute) -"@; }@" - $($unit.SingularName), -"@; }@" - } - - #pragma warning restore 1591 -} -"@; -} diff --git a/UnitsNet.WindowsRuntimeComponent/Scripts/Types.psm1 b/UnitsNet.WindowsRuntimeComponent/Scripts/Types.psm1 deleted file mode 100644 index 7dfae70090..0000000000 --- a/UnitsNet.WindowsRuntimeComponent/Scripts/Types.psm1 +++ /dev/null @@ -1,50 +0,0 @@ -class Quantity -{ - [string]$Name - [string]$XmlDocSummary - [string]$XmlDocRemarks - [string]$BaseUnit - [string]$BaseType # TODO Rename me to ValueType - [BaseDimensions]$BaseDimensions = [BaseDimensions]::new() - [boolean]$GenerateArithmetic = $true - [boolean]$Logarithmic = $false - [int]$LogarithmicScalingFactor = 1 - [Unit[]]$Units = @() -} - -class Unit -{ - [string]$SingularName - [string]$PluralName - [string]$XmlDocSummary - [string]$XmlDocRemarks - [string]$FromUnitToBaseFunc - [string]$FromBaseToUnitFunc - [string[]]$Prefixes = @() - [Localization[]]$Localization = @() -} - -class Localization -{ - [string]$Culture - [string[]]$Abbreviations = @() - [object[]]$AbbreviationsWithPrefixes = @() -} - -class BaseDimensions -{ - [int]$Length = 0 - [int]$Mass = 0 - [int]$Time = 0 - [int]$ElectricCurrent = 0 - [int]$Temperature = 0 - [int]$AmountOfSubstance = 0 - [int]$LuminousIntensity = 0 -} - -class GeneratorArgs -{ - [Quantity]$Quantity - [Unit]$BaseUnit - [string]$UnitEnumName -} diff --git a/UnitsNet.sln b/UnitsNet.sln index f827f95b70..4553cef60d 100644 --- a/UnitsNet.sln +++ b/UnitsNet.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitsNet.Tests", "UnitsNet. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitsNet.Serialization.JsonNet.CompatibilityTests", "UnitsNet.Serialization.JsonNet.CompatibilityTests\UnitsNet.Serialization.JsonNet.CompatibilityTests.csproj", "{21F2FFAC-BF39-487F-9ADE-37100162F955}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGen", "CodeGen\CodeGen.csproj", "{078E3D44-4F60-46B3-9099-91A7CBF0B213}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {21F2FFAC-BF39-487F-9ADE-37100162F955}.Debug|Any CPU.Build.0 = Debug|Any CPU {21F2FFAC-BF39-487F-9ADE-37100162F955}.Release|Any CPU.ActiveCfg = Release|Any CPU {21F2FFAC-BF39-487F-9ADE-37100162F955}.Release|Any CPU.Build.0 = Release|Any CPU + {078E3D44-4F60-46B3-9099-91A7CBF0B213}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {078E3D44-4F60-46B3-9099-91A7CBF0B213}.Debug|Any CPU.Build.0 = Debug|Any CPU + {078E3D44-4F60-46B3-9099-91A7CBF0B213}.Release|Any CPU.ActiveCfg = Release|Any CPU + {078E3D44-4F60-46B3-9099-91A7CBF0B213}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UnitsNet.sln.DotSettings b/UnitsNet.sln.DotSettings index 40954ed83d..0990561af3 100644 --- a/UnitsNet.sln.DotSettings +++ b/UnitsNet.sln.DotSettings @@ -17,4 +17,7 @@ True True True - True + True + True + True + True diff --git a/UnitsNet/GeneratedCode/UnitAbbreviationsCache.g.cs b/UnitsNet/GeneratedCode/UnitAbbreviationsCache.g.cs index c2915b9da4..c25849931d 100644 --- a/UnitsNet/GeneratedCode/UnitAbbreviationsCache.g.cs +++ b/UnitsNet/GeneratedCode/UnitAbbreviationsCache.g.cs @@ -71,18 +71,15 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Degree, new string[]{"°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Gradian, new string[]{"g"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Gradian, new string[]{"g"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µ°"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µdeg"}), + ("en-US", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µ°", "µdeg"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Microdegree, new string[]{"µ°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Microradian, new string[]{"µrad"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Microradian, new string[]{"µрад"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"m°"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"mdeg"}), + ("en-US", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"m°", "mdeg"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Millidegree, new string[]{"m°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Milliradian, new string[]{"mrad"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Milliradian, new string[]{"mрад"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"n°"}), - ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"ndeg"}), + ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"n°", "ndeg"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Nanodegree, new string[]{"n°"}), ("en-US", typeof(AngleUnit), (int)AngleUnit.Nanoradian, new string[]{"nrad"}), ("ru-RU", typeof(AngleUnit), (int)AngleUnit.Nanoradian, new string[]{"nрад"}), @@ -129,41 +126,29 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(AreaMomentOfInertiaUnit), (int)AreaMomentOfInertiaUnit.MillimeterToTheFourth, new string[]{"mm⁴", "mm^4"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.BitPerSecond, new string[]{"bit/s", "bps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.BytePerSecond, new string[]{"B/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabitPerSecond, new string[]{"Ebit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabitPerSecond, new string[]{"Ebps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabitPerSecond, new string[]{"Ebit/s", "Ebps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExabytePerSecond, new string[]{"EB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibitPerSecond, new string[]{"Eibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibitPerSecond, new string[]{"Eibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibitPerSecond, new string[]{"Eibit/s", "Eibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.ExbibytePerSecond, new string[]{"EiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibitPerSecond, new string[]{"Gibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibitPerSecond, new string[]{"Gibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibitPerSecond, new string[]{"Gibit/s", "Gibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GibibytePerSecond, new string[]{"GiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabitPerSecond, new string[]{"Gbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabitPerSecond, new string[]{"Gbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabitPerSecond, new string[]{"Gbit/s", "Gbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.GigabytePerSecond, new string[]{"GB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibitPerSecond, new string[]{"Kibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibitPerSecond, new string[]{"Kibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibitPerSecond, new string[]{"Kibit/s", "Kibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KibibytePerSecond, new string[]{"KiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobitPerSecond, new string[]{"kbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobitPerSecond, new string[]{"kbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobitPerSecond, new string[]{"kbit/s", "kbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.KilobytePerSecond, new string[]{"kB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibitPerSecond, new string[]{"Mibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibitPerSecond, new string[]{"Mibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibitPerSecond, new string[]{"Mibit/s", "Mibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MebibytePerSecond, new string[]{"MiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabitPerSecond, new string[]{"Mbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabitPerSecond, new string[]{"Mbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabitPerSecond, new string[]{"Mbit/s", "Mbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.MegabytePerSecond, new string[]{"MB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibitPerSecond, new string[]{"Pibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibitPerSecond, new string[]{"Pibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibitPerSecond, new string[]{"Pibit/s", "Pibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PebibytePerSecond, new string[]{"PiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabitPerSecond, new string[]{"Pbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabitPerSecond, new string[]{"Pbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabitPerSecond, new string[]{"Pbit/s", "Pbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.PetabytePerSecond, new string[]{"PB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibitPerSecond, new string[]{"Tibit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibitPerSecond, new string[]{"Tibps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibitPerSecond, new string[]{"Tibit/s", "Tibps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TebibytePerSecond, new string[]{"TiB/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabitPerSecond, new string[]{"Tbit/s"}), - ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabitPerSecond, new string[]{"Tbps"}), + ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabitPerSecond, new string[]{"Tbit/s", "Tbps"}), ("en-US", typeof(BitRateUnit), (int)BitRateUnit.TerabytePerSecond, new string[]{"TB/s"}), ("en-US", typeof(BrakeSpecificFuelConsumptionUnit), (int)BrakeSpecificFuelConsumptionUnit.GramPerKiloWattHour, new string[]{"g/kWh"}), ("en-US", typeof(BrakeSpecificFuelConsumptionUnit), (int)BrakeSpecificFuelConsumptionUnit.KilogramPerJoule, new string[]{"kg/J"}), @@ -226,37 +211,16 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Day, new string[]{"д"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Hour, new string[]{"h", "hr", "hrs", "hour", "hours"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Hour, new string[]{"ч"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µsec"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µsecs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µsecond"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µseconds"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мкс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мксек"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мкс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мксек"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"ms"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"msec"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"msecs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"msecond"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"mseconds"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мсек"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мсек"}), + ("en-US", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"µs", "µsec", "µsecs", "µsecond", "µseconds"}), + ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Microsecond, new string[]{"мкс", "мксек"}), + ("en-US", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"ms", "msec", "msecs", "msecond", "mseconds"}), + ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Millisecond, new string[]{"мс", "мсек"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Minute, new string[]{"m", "min", "minute", "minutes"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Minute, new string[]{"мин"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Month30, new string[]{"mo", "month", "months"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Month30, new string[]{"месяц"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"ns"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nsec"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nsecs"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nsecond"}), - ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"nseconds"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нсек"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нс"}), - ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нсек"}), + ("en-US", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"ns", "nsec", "nsecs", "nsecond", "nseconds"}), + ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Nanosecond, new string[]{"нс", "нсек"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Second, new string[]{"s", "sec", "secs", "second", "seconds"}), ("ru-RU", typeof(DurationUnit), (int)DurationUnit.Second, new string[]{"с", "сек"}), ("en-US", typeof(DurationUnit), (int)DurationUnit.Week, new string[]{"wk", "week", "weeks"}), @@ -592,18 +556,14 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(MassUnit), (int)MassUnit.Hectogram, new string[]{"гг"}), ("en-US", typeof(MassUnit), (int)MassUnit.Kilogram, new string[]{"kg"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Kilogram, new string[]{"кг"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klb"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klbs"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klbm"}), + ("en-US", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"klb", "klbs", "klbm"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Kilopound, new string[]{"kфунт"}), ("en-US", typeof(MassUnit), (int)MassUnit.Kilotonne, new string[]{"kt"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Kilotonne, new string[]{"кт"}), ("en-US", typeof(MassUnit), (int)MassUnit.LongHundredweight, new string[]{"cwt"}), ("en-US", typeof(MassUnit), (int)MassUnit.LongTon, new string[]{"long tn"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.LongTon, new string[]{"тонна большая"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlb"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlbs"}), - ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlbm"}), + ("en-US", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mlb", "Mlbs", "Mlbm"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Megapound, new string[]{"Mфунт"}), ("en-US", typeof(MassUnit), (int)MassUnit.Megatonne, new string[]{"Mt"}), ("ru-RU", typeof(MassUnit), (int)MassUnit.Megatonne, new string[]{"Мт"}), @@ -760,20 +720,14 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(MolarEntropyUnit), (int)MolarEntropyUnit.JoulePerMoleKelvin, new string[]{"J/(mol*K)"}), ("en-US", typeof(MolarEntropyUnit), (int)MolarEntropyUnit.KilojoulePerMoleKelvin, new string[]{"kJ/(mol*K)"}), ("en-US", typeof(MolarEntropyUnit), (int)MolarEntropyUnit.MegajoulePerMoleKelvin, new string[]{"MJ/(mol*K)"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.CentimolesPerLiter, new string[]{"cmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.CentimolesPerLiter, new string[]{"cM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.DecimolesPerLiter, new string[]{"dmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.DecimolesPerLiter, new string[]{"dM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MicromolesPerLiter, new string[]{"µmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MicromolesPerLiter, new string[]{"µM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MillimolesPerLiter, new string[]{"mmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MillimolesPerLiter, new string[]{"mM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.CentimolesPerLiter, new string[]{"cmol/L", "cM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.DecimolesPerLiter, new string[]{"dmol/L", "dM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MicromolesPerLiter, new string[]{"µmol/L", "µM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MillimolesPerLiter, new string[]{"mmol/L", "mM"}), ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MolesPerCubicMeter, new string[]{"mol/m³"}), ("en-US", typeof(MolarityUnit), (int)MolarityUnit.MolesPerLiter, new string[]{"mol/L", "M"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.NanomolesPerLiter, new string[]{"nmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.NanomolesPerLiter, new string[]{"nM"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.PicomolesPerLiter, new string[]{"pmol/L"}), - ("en-US", typeof(MolarityUnit), (int)MolarityUnit.PicomolesPerLiter, new string[]{"pM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.NanomolesPerLiter, new string[]{"nmol/L", "nM"}), + ("en-US", typeof(MolarityUnit), (int)MolarityUnit.PicomolesPerLiter, new string[]{"pmol/L", "pM"}), ("en-US", typeof(MolarMassUnit), (int)MolarMassUnit.CentigramPerMole, new string[]{"cg/mol"}), ("ru-RU", typeof(MolarMassUnit), (int)MolarMassUnit.CentigramPerMole, new string[]{"сг/моль"}), ("en-US", typeof(MolarMassUnit), (int)MolarMassUnit.DecagramPerMole, new string[]{"dag/mol"}), @@ -902,7 +856,6 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(PressureUnit), (int)PressureUnit.Kilopascal, new string[]{"кПа"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.KilopoundForcePerSquareFoot, new string[]{"kipf/ft²"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.KilopoundForcePerSquareInch, new string[]{"kipf/in²"}), - ("en-US", typeof(PressureUnit), (int)PressureUnit.KilopoundForcePerSquareInch, new string[]{"kipf/in²"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.Megabar, new string[]{"Mbar"}), ("ru-RU", typeof(PressureUnit), (int)PressureUnit.Megabar, new string[]{"Mбар"}), ("en-US", typeof(PressureUnit), (int)PressureUnit.MeganewtonPerSquareMeter, new string[]{"MN/m²"}), @@ -974,18 +927,15 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.DegreePerMinute, new string[]{"°/min", "deg/min"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.DegreePerSecond, new string[]{"°/s", "deg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.DegreePerSecond, new string[]{"°/с"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µ°/s"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µdeg/s"}), + ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µ°/s", "µdeg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicrodegreePerSecond, new string[]{"µ°/с"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicroradianPerSecond, new string[]{"µrad/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MicroradianPerSecond, new string[]{"µрад/с"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"m°/s"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"mdeg/s"}), + ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"m°/s", "mdeg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MillidegreePerSecond, new string[]{"m°/с"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MilliradianPerSecond, new string[]{"mrad/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.MilliradianPerSecond, new string[]{"mрад/с"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"n°/s"}), - ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"ndeg/s"}), + ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"n°/s", "ndeg/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanodegreePerSecond, new string[]{"n°/с"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanoradianPerSecond, new string[]{"nrad/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.NanoradianPerSecond, new string[]{"nрад/с"}), @@ -995,15 +945,11 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.RevolutionPerMinute, new string[]{"об/мин"}), ("en-US", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.RevolutionPerSecond, new string[]{"r/s"}), ("ru-RU", typeof(RotationalSpeedUnit), (int)RotationalSpeedUnit.RevolutionPerSecond, new string[]{"об/с"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.KilonewtonMeterPerRadian, new string[]{"kN·m/rad"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.KilonewtonMeterPerRadian, new string[]{"kNm/rad"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.MeganewtonMeterPerRadian, new string[]{"MN·m/rad"}), - ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.MeganewtonMeterPerRadian, new string[]{"MNm/rad"}), + ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.KilonewtonMeterPerRadian, new string[]{"kN·m/rad", "kNm/rad"}), + ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.MeganewtonMeterPerRadian, new string[]{"MN·m/rad", "MNm/rad"}), ("en-US", typeof(RotationalStiffnessUnit), (int)RotationalStiffnessUnit.NewtonMeterPerRadian, new string[]{"N·m/rad", "Nm/rad"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.KilonewtonMeterPerRadianPerMeter, new string[]{"kN·m/rad/m"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.KilonewtonMeterPerRadianPerMeter, new string[]{"kNm/rad/m"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.MeganewtonMeterPerRadianPerMeter, new string[]{"MN·m/rad/m"}), - ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.MeganewtonMeterPerRadianPerMeter, new string[]{"MNm/rad/m"}), + ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.KilonewtonMeterPerRadianPerMeter, new string[]{"kN·m/rad/m", "kNm/rad/m"}), + ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.MeganewtonMeterPerRadianPerMeter, new string[]{"MN·m/rad/m", "MNm/rad/m"}), ("en-US", typeof(RotationalStiffnessPerLengthUnit), (int)RotationalStiffnessPerLengthUnit.NewtonMeterPerRadianPerMeter, new string[]{"N·m/rad/m", "Nm/rad/m"}), ("en-US", typeof(SolidAngleUnit), (int)SolidAngleUnit.Steradian, new string[]{"sr"}), ("en-US", typeof(SpecificEnergyUnit), (int)SpecificEnergyUnit.BtuPerPound, new string[]{"btu/lb"}), @@ -1247,9 +1193,7 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.AcreFootPerHour, new string[]{"af/h"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.AcreFootPerMinute, new string[]{"af/m"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.AcreFootPerSecond, new string[]{"af/s"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerDay, new string[]{"cl/day", "cL/d", "cLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CentiliterPerMinute, new string[]{"cLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicDecimeterPerMinute, new string[]{"dm³/min"}), ("ru-RU", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicDecimeterPerMinute, new string[]{"дм³/мин"}), @@ -1268,13 +1212,9 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicYardPerHour, new string[]{"yd³/h"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicYardPerMinute, new string[]{"yd³/min"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.CubicYardPerSecond, new string[]{"yd³/s"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerDay, new string[]{"dl/day", "dL/d", "dLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.DeciliterPerMinute, new string[]{"dLPM"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerDay, new string[]{"kl/day", "kL/d", "kLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KiloliterPerMinute, new string[]{"kLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.KilousGallonPerMinute, new string[]{"kgal (U.S.)/min", "KGPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerDay, new string[]{"l/day", "L/d", "LPD"}), @@ -1282,22 +1222,14 @@ private static readonly (string CultureName, Type UnitType, int UnitValue, strin ("ru-RU", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerHour, new string[]{"л/ч"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerMinute, new string[]{"LPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.LiterPerSecond, new string[]{"LPS"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"Ml/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"ML/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"MLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaliterPerDay, new string[]{"Ml/day", "ML/d", "MLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MegaukGallonPerSecond, new string[]{"Mgal (imp.)/s"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerDay, new string[]{"µl/day", "µL/d", "µLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MicroliterPerMinute, new string[]{"µLPM"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"ml/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"mL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"mLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerDay, new string[]{"ml/day", "mL/d", "mLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MilliliterPerMinute, new string[]{"mLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.MillionUsGallonsPerDay, new string[]{"MGD"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nl/day"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nL/d"}), - ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nLPD"}), + ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerDay, new string[]{"nl/day", "nL/d", "nLPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.NanoliterPerMinute, new string[]{"nLPM"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.OilBarrelPerDay, new string[]{"bbl/d", "BOPD"}), ("en-US", typeof(VolumeFlowUnit), (int)VolumeFlowUnit.OilBarrelPerHour, new string[]{"bbl/hr", "bph"}), diff --git a/UnitsNet/Scripts/GenerateUnits.ps1 b/UnitsNet/Scripts/GenerateUnits.ps1 deleted file mode 100644 index ca7a0223b9..0000000000 --- a/UnitsNet/Scripts/GenerateUnits.ps1 +++ /dev/null @@ -1,358 +0,0 @@ -using module ".\Types.psm1" - -#Requires -Version 5.1 - -# Set Write-Output used by Include- files to UTF8 encoding to fix copyright character -[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8 -$OutputEncoding = [Text.UTF8Encoding]::UTF8 - -# DaddyCool => daddyCool -function ToCamelCase($str) -{ - return $str.Substring(0,1).ToLowerInvariant() + $str.Substring(1) -} - -function ValueOrDefault($value, $defaultValue){ - if ($null -ne $value) { $value } else { $defaultValue } -} - -function Ternary($value, $one, $two){ - if ($value -ne $null) { $one } else { $two } -} - -function GenerateQuantity([Quantity]$quantity, $outDir) -{ - $outFileName = "$outDir/$($quantity.Name).NetFramework.g.cs" - GenerateQuantitySourceCode $quantity | Out-File -Encoding "UTF8" $outFileName | Out-Null - if (!$?) { exit 1 } - Write-Host -NoNewline "quantity(OK) " -} - -function GenerateUnitTestBaseClass([Quantity]$quantity, $outDir) -{ - $outFileName = "$outDir/$($quantity.Name)TestsBase.g.cs" - GenerateUnitTestBaseClassSourceCode $quantity | Out-File -Encoding "UTF8" $outFileName | Out-Null - if (!$?) { - exit 1 - } - Write-Host -NoNewline "test base(OK) " -} - -function GenerateUnitTestClassIfNotExists([Quantity]$quantity, $outDir) -{ - Write-Host -NoNewline "test stub" - $outFileName = "$outDir/$($quantity.Name)Tests.cs" - if (Test-Path $outFileName) - { - Write-Host -NoNewline "(skip) " - return - } - else - { - GenerateUnitTestPlaceholderSourceCode $quantity | Out-File -Encoding "UTF8" $outFileName | Out-Null - if (!$?) { - exit 1 - } - Write-Host -NoNewline "(OK) " - } -} - -function GenerateUnitType([Quantity]$quantity, $outDir) -{ - $outFileName = "$outDir/$($quantity.Name)Unit.g.cs" - - GenerateUnitTypeSourceCode $quantity | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - exit 1 - } - Write-Host -NoNewline "unit(OK) " -} - -function GenerateUnitSystemDefault($quantities, $outDir) -{ - Write-Host -NoNewline "UnitAbbreviationsCache.g.cs: " - $outFileName = "$outDir/UnitAbbreviationsCache.g.cs" - - GenerateUnitSystemDefaultSourceCode $quantities | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - Write-Host "(error) " - exit 1 - } - Write-Host "(OK) " -} - -function GenerateQuantityType($quantities, $outDir) -{ - Write-Host -NoNewline "QuantityType.g.cs: " - $outFileName = "$outDir/QuantityType.g.cs" - - GenerateQuantityTypeSourceCode $quantities | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - Write-Host "(error) " - exit 1 - } - Write-Host "(OK) " -} - -function GenerateStaticQuantity($quantities, $outDir) -{ - Write-Host -NoNewline "Quantity.g.cs: " - $outFileName = "$outDir/Quantity.g.cs" - - GenerateStaticQuantitySourceCode $quantities | Out-File -Encoding "UTF8" -Force $outFileName | Out-Null - if (!$?) { - Write-Host "(error) " - exit 1 - } - Write-Host "(OK) " -} - -function EnsureDirExists([String] $dirPath) { - New-Item -ItemType Directory -Force -Path $dirPath | Out-Null - if (!$?) { - exit 1 - } -} - -function Set-ConversionFunctions -{ - param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $quantity) - PROCESS { - foreach ($u in $quantity.Units) { - - # Use decimal for internal calculations if base type is not double, such as for long or int. - if ($quantity.BaseType -eq "decimal") { - $u.FromUnitToBaseFunc = $u.FromUnitToBaseFunc -replace "d", "m" - $u.FromBaseToUnitFunc = $u.FromBaseToUnitFunc -replace "d", "m" - } - - # Convert to/from double for other base types - $u.FromUnitToBaseFunc = "$($u.FromUnitToBaseFunc)" - $u.FromBaseToUnitFunc = "$($u.FromBaseToUnitFunc)" - } - return $quantity - } -} - -function Add-PrefixUnits { - param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $quantity) - PROCESS { - $prefixUnits = @() - - foreach ($unit in $quantity.Units) - { - foreach ($localization in $unit.Localization){ - if($localization.AbbreviationsWithPrefixes.Count -gt 0){ - if($unit.Prefixes.Count -ne $localization.AbbreviationsWithPrefixes.Count){ - Write-Error "The prefix count ($($unit.Prefixes.Count)) does not match the abbreviations with prefixes count ($($localization.AbbreviationsWithPrefixes.Count)) for $($quantity.Name).$($unit.SingularName)" -ErrorAction Stop - } - } - } - - $prefixIndex = 0 - foreach ($prefix in $unit.Prefixes) - { - $prefixInfo = switch ($prefix) - { - "Kilo" { "k", "1e3d"; break; } - "Hecto" { "h", "1e2d"; break; } - "Deca" { "da", "1e1d"; break; } - "Deci" { "d", "1e-1d"; break; } - "Centi" { "c", "1e-2d"; break; } - "Milli" { "m", "1e-3d"; break; } - "Micro" { "µ", "1e-6d"; break; } - "Nano" { "n", "1e-9d"; break; } - - # Optimization, move less frequently used prefixes to the end - "Pico" { "p", "1e-12d"; break; } - "Femto" { "f", "1e-15d"; break; } - "Atto" { "a", "1e-18d"; break; } - "Zepto" { "z", "1e-21d"; break; } - "Yocto" { "y", "1e-24d"; break; } - - "Yotta" { "Y", "1e24d"; break; } - "Zetta" { "Z", "1e21d"; break; } - "Exa" { "E", "1e18d"; break; } - "Peta" { "P", "1e15d"; break; } - "Tera" { "T", "1e12d"; break; } - "Giga" { "G", "1e9d"; break; } - "Mega" { "M", "1e6d"; break; } - - # Binary prefixes - "Kibi" { "Ki", "1024d"; break; } - "Mebi" { "Mi", "(1024d * 1024)"; break; } - "Gibi" { "Gi", "(1024d * 1024 * 1024)"; break; } - "Tebi" { "Ti", "(1024d * 1024 * 1024 * 1024)"; break; } - "Pebi" { "Pi", "(1024d * 1024 * 1024 * 1024 * 1024)"; break; } - "Exbi" { "Ei", "(1024d * 1024 * 1024 * 1024 * 1024 * 1024)"; break; } - } - - $prefixAbbreviation = $prefixInfo[0] - $prefixFactor = $prefixInfo[1] - - $prefixUnit = New-Object PsObject -Property @{ - SingularName=$prefix + $(ToCamelCase $unit.SingularName) - PluralName=$prefix + $(ToCamelCase $unit.PluralName) - BaseUnits = $null - FromUnitToBaseFunc="("+$unit.FromUnitToBaseFunc+") * $prefixFactor" - FromBaseToUnitFunc="("+$unit.FromBaseToUnitFunc+") / $prefixFactor" - - Localization=$unit.Localization | % { - foreach ($abbrSyno in $_.Abbreviations) { - $abbrev = $prefixAbbreviation + $abbrSyno - if ($_.AbbreviationsWithPrefixes) { - if($_.AbbreviationsWithPrefixes[$prefixIndex] -isnot [System.String]){ - foreach($synoWithPrefix in $_.AbbreviationsWithPrefixes[$prefixIndex]){ - New-Object PsObject -Property @{ - Culture=$_.Culture - Abbreviations= $synoWithPrefix - } - } - continue - } - else{ - $abbrev = $_.AbbreviationsWithPrefixes[$prefixIndex] - } - } - New-Object PsObject -Property @{ - Culture=$_.Culture - Abbreviations= $abbrev - } - } - } - } - - # Append prefix unit - $prefixUnits += $prefixUnit - $prefixIndex++; - } # foreach prefixes - } # foreach units - - $quantity.Units += $prefixUnits - return $quantity - } -} - -function Set-UnitsOrderedByName { - param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $quantity) - PROCESS { - $quantity.Units = ($quantity.Units | sort SingularName) - return $quantity - } -} - -function Add-InheritedUnits([Quantity]$quantity, $quantities) { - - foreach ($inheritFromQuantityName in $quantity.InheritUnitsFrom) { - $inheritFromQuantity = $quantities | Where { $_.Name -eq $inheritFromQuantityName } | Select -First 1 - $quantity.Units += $inheritFromQuantity.Units - - Write-Host -NoNewline "(inherit $inheritFromQuantityName) " - } -} - -# Load external generator functions with same name as file -. "$PSScriptRoot/Include-GenerateTemplates.ps1" -. "$PSScriptRoot/Include-GenerateUnitSystemDefaultSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateQuantityTypeSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateStaticQuantitySourceCode.ps1" -. "$PSScriptRoot/Include-GenerateQuantitySourceCode.ps1" -. "$PSScriptRoot/Include-GenerateUnitTypeSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateUnitTestBaseClassSourceCode.ps1" -. "$PSScriptRoot/Include-GenerateUnitTestPlaceholderSourceCode.ps1" - -EnsureDirExists ($quantityDir = "$PSScriptRoot/../GeneratedCode/Quantities") -EnsureDirExists ($unitEnumDir = "$PSScriptRoot/../GeneratedCode/Units") -EnsureDirExists ($unitSystemDir = "$PSScriptRoot/../GeneratedCode") -EnsureDirExists ($testsDir = "$PSScriptRoot/../../UnitsNet.Tests/GeneratedCode") -EnsureDirExists ($testsCustomCodeDir = "$PSScriptRoot/../../UnitsNet.Tests/CustomCode") - -$templatesDir = "$PSScriptRoot/../../Common/UnitDefinitions" -$pad = 25 - -# Parse unit definitions from .json files -# TODO Find a way to automap from JSON into Quantity type -$quantities = Get-ChildItem -Path $templatesDir -filter "*.json" ` - | %{(Get-Content $_.FullName -Encoding "UTF8" | Out-String)} ` - | ConvertFrom-Json ` - | %{ - # $_ | fl | out-string | write-host -foreground blue - # New-Object -TypeName Quantity -Verbose -Property @{ - [Quantity]@{ - Name = $_.Name - XmlDocSummary = $_.XmlDoc - XmlDocRemarks = $_.XmlDocRemarks - BaseUnit = $_.BaseUnit - BaseType = ValueOrDefault $_.BaseType "double" - BaseDimensions = @{ - Length = ValueOrDefault $_.BaseDimensions.L 0 - Mass = ValueOrDefault $_.BaseDimensions.M 0 - Time = ValueOrDefault $_.BaseDimensions.T 0 - ElectricCurrent = ValueOrDefault $_.BaseDimensions.I 0 - Temperature = ValueOrDefault $_.BaseDimensions.Θ 0 - AmountOfSubstance = ValueOrDefault $_.BaseDimensions.N 0 - LuminousIntensity = ValueOrDefault $_.BaseDimensions.J 0 - } - GenerateArithmetic = ValueOrDefault $_.GenerateArithmetic $true - Logarithmic = ValueOrDefault $_.Logarithmic $false - LogarithmicScalingFactor = ValueOrDefault $_.LogarithmicScalingFactor 1 - Units = $_.Units | %{ - # $_ | fl | out-string | Write-Host -ForegroundColor blue - [Unit]@{ - SingularName = $_.SingularName - PluralName = $_.PluralName - BaseUnits = Ternary $_.BaseUnits @{ - # $_ | fl | out-string | Write-Host -ForegroundColor green - Length = Ternary $_.BaseUnits.L "length: LengthUnit.$($_.BaseUnits.L)" $null - Mass = Ternary $_.BaseUnits.M "mass: MassUnit.$($_.BaseUnits.M)" $null - Time = Ternary $_.BaseUnits.T "time: DurationUnit.$($_.BaseUnits.T)" $null - ElectricCurrent = Ternary $_.BaseUnits.I "current: ElectricCurrentUnit.$($_.BaseUnits.I)" $null - Temperature = Ternary $_.BaseUnits.Θ "temperature: TemperatureUnit.$($_.BaseUnits.Θ)" $null - AmountOfSubstance = Ternary $_.BaseUnits.N "amount: AmountOfSubstanceUnit.$($_.BaseUnits.N)" $null - LuminousIntensity = Ternary $_.BaseUnits.J "luminousIntensity: LuminousIntensityUnit.$($_.BaseUnits.J)" $null - } $null - XmlDocSummary = $_.XmlDocSummary - XmlDocRemarks = $_.XmlDocRemarks - FromUnitToBaseFunc = $_.FromUnitToBaseFunc - FromBaseToUnitFunc = $_.FromBaseToUnitFunc - Prefixes = [string[]](ValueOrDefault $_.Prefixes @()) - Localization = $_.Localization | %{ - # $_ | fl | out-string | Write-Host -ForegroundColor blue - [Localization]@{ - Culture = $_.Culture - Abbreviations = $_.Abbreviations - AbbreviationsWithPrefixes = $_.AbbreviationsWithPrefixes - } - } - } - } - } - } ` - | Add-PrefixUnits ` - | Set-ConversionFunctions ` - | Set-UnitsOrderedByName - -foreach ($quantity in $quantities) { - Write-Host -NoNewline "$($quantity.Name):".PadRight($pad) - - Add-InheritedUnits $quantity $quantities - - GenerateQuantity $quantity $quantityDir - GenerateUnitType $quantity $unitEnumDir - GenerateUnitTestBaseClass $quantity $testsDir - GenerateUnitTestClassIfNotExists $quantity $testsCustomCodeDir - - Write-Host "" -} - -Write-Host "" -GenerateUnitSystemDefault $quantities $unitSystemDir -GenerateQuantityType $quantities $unitSystemDir -GenerateStaticQuantity $quantities $unitSystemDir - -$unitCount = ($quantities | %{$_.Units.Count} | Measure -Sum).Sum - -Write-Host "`n`n" -Write-Host -Foreground Yellow "Summary: $unitCount units in $($quantities.Count) quantities".PadRight($pad) -Write-Host "`n`n" -exit 0 diff --git a/UnitsNet/Scripts/Include-GenerateQuantitySourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateQuantitySourceCode.ps1 deleted file mode 100644 index be9b1228fa..0000000000 --- a/UnitsNet/Scripts/Include-GenerateQuantitySourceCode.ps1 +++ /dev/null @@ -1,1093 +0,0 @@ -using module ".\Types.psm1" - -function GenerateQuantitySourceCode([Quantity]$quantity) -{ - $quantityName = $quantity.Name; - $units = $quantity.Units; - $valueType = $quantity.BaseType; - [Unit]$baseUnit = $units | where { $_.SingularName -eq $quantity.BaseUnit } | Select-Object -First 1 - $baseUnitSingularName = $baseUnit.SingularName - $baseUnitPluralName = $baseUnit.PluralName - $unitEnumName = "$quantityName" + "Unit" - - $baseDimensions = $quantity.BaseDimensions; - $isDimensionless = $baseDimensions -eq $null -or ( $baseDimensions.Length -eq 0 -and $baseDimensions.Mass -eq 0 -and $baseDimensions.Time -eq 0 -and $baseDimensions.ElectricCurrent -eq 0 -and $baseDimensions.Temperature -eq 0 -and $baseDimensions.AmountOfSubstance -eq 0 -and $baseDimensions.LuminousIntensity -eq 0 ) - - [GeneratorArgs]$genArgs = New-Object GeneratorArgs -Property @{ - Quantity = $quantity; - BaseUnit = $baseUnit; - UnitEnumName = $unitEnumName; - } - # $genArgs | fl | out-string | write-host -foreground yellow -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using System.Globalization; -using System.Linq; -using JetBrains.Annotations; -using UnitsNet.InternalHelpers; -using UnitsNet.Units; - -// ReSharper disable once CheckNamespace - -namespace UnitsNet -{ -"@; -$obsoleteAttribute = GetObsoleteAttribute($quantity); -if ($obsoleteAttribute) -{ - $obsoleteAttribute = "`r`n " + $obsoleteAttribute; # apply padding to conformance with code format in this section -} -@" - /// - /// - /// $($quantity.XmlDocSummary) - /// -"@; if ($quantity.XmlDocRemarks) {@" - /// - /// $($quantity.XmlDocRemarks) - /// -"@; }@" - public partial struct $quantityName : IQuantity<$unitEnumName>, IEquatable<$quantityName>, IComparable, IComparable<$quantityName>, IConvertible, IFormattable - { - /// - /// The numeric value this quantity was constructed with. - /// - private readonly $valueType _value; - - /// - /// The unit this quantity was constructed with. - /// - private readonly $($unitEnumName)? _unit; - - static $quantityName() - { -"@; if($isDimensionless) {@" - BaseDimensions = BaseDimensions.Dimensionless; -"@; } else {@" - BaseDimensions = new BaseDimensions($($baseDimensions.Length), $($baseDimensions.Mass), $($baseDimensions.Time), $($baseDimensions.ElectricCurrent), $($baseDimensions.Temperature), $($baseDimensions.AmountOfSubstance), $($baseDimensions.LuminousIntensity)); -"@; }@" - - Info = new QuantityInfo<$unitEnumName>(QuantityType.$quantityName, - new UnitInfo<$unitEnumName>[] { -"@; foreach ($unit in $units) { - if($unit.BaseUnits -eq $null){@" - new UnitInfo<$unitEnumName>($unitEnumName.$($unit.SingularName), BaseUnits.Undefined), -"@; } else{ - $baseUnitsArray = @($unit.BaseUnits.Length, $unit.BaseUnits.Mass, $unit.BaseUnits.Time, $unit.BaseUnits.ElectricCurrent, $unit.BaseUnits.Temperature, $unit.BaseUnits.AmountOfSubstance, $unit.BaseUnits.LuminousIntensity) - $baseUnitsArrayFiltered = $baseUnitsArray | Where-Object {$_ -ne ""} - $baseUnitsConstructorString = $baseUnitsArrayFiltered -join ', ';@" - new UnitInfo<$unitEnumName>($unitEnumName.$($unit.SingularName), new BaseUnits($baseUnitsConstructorString)), -"@; } - }@" - }, - BaseUnit, Zero, BaseDimensions); - } - - /// - /// Creates the quantity with the given numeric value and unit. - /// - /// The numeric value to contruct this quantity with. - /// The unit representation to contruct this quantity with. - /// If value is NaN or Infinity. - public $quantityName($valueType numericValue, $unitEnumName unit) - { - if(unit == $unitEnumName.Undefined) - throw new ArgumentException("The quantity can not be created with an undefined unit.", nameof(unit)); - -"@; if ($quantity.BaseType -eq "double") {@" - _value = Guard.EnsureValidNumber(numericValue, nameof(numericValue)); -"@; } else {@" - _value = numericValue; -"@; }@" - _unit = unit; - } - - /// - /// Creates an instance of the quantity with the given numeric value in units compatible with the given . - /// If multiple compatible units were found, the first match is used. - /// - /// The numeric value to contruct this quantity with. - /// The unit system to create the quantity with. - /// The given is null. - /// No unit was found for the given . - public $quantityName($valueType numericValue, UnitSystem unitSystem) - { - if(unitSystem == null) throw new ArgumentNullException(nameof(unitSystem)); - - var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits); - var firstUnitInfo = unitInfos.FirstOrDefault(); - -"@; if ($quantity.BaseType -eq "double") {@" - _value = Guard.EnsureValidNumber(numericValue, nameof(numericValue)); -"@; } else {@" - _value = numericValue; -"@; }@" - _unit = firstUnitInfo?.Value ?? throw new ArgumentException("No units were found for the given UnitSystem.", nameof(unitSystem)); - } -"@; - GenerateStaticProperties $genArgs - GenerateProperties $genArgs - GenerateConversionProperties $genArgs - GenerateStaticMethods $genArgs - GenerateStaticFactoryMethods $genArgs - GenerateStaticParseMethods $genArgs - GenerateArithmeticOperators $genArgs - GenerateEqualityAndComparison $genArgs - GenerateConversionMethods $genArgs -@" - - #region ToString Methods - - /// - /// Gets the default string representation of value and unit. - /// - /// String representation. - public override string ToString() - { - return ToString("g"); - } - - /// - /// Gets the default string representation of value and unit using the given format provider. - /// - /// String representation. - /// Format to use for localization and number formatting. Defaults to if null. - public string ToString([CanBeNull] IFormatProvider provider) - { - return ToString("g", provider); - } - - /// - /// Get string representation of value and unit. - /// - /// The number of significant digits after the radix point. - /// String representation. - /// Format to use for localization and number formatting. Defaults to if null. - [Obsolete(@"This method is deprecated and will be removed at a future release. Please use ToString(""s2"") or ToString(""s2"", provider) where 2 is an example of the number passed to significantDigitsAfterRadix.")] - public string ToString([CanBeNull] IFormatProvider provider, int significantDigitsAfterRadix) - { - var value = Convert.ToDouble(Value); - var format = UnitFormatter.GetFormat(value, significantDigitsAfterRadix); - return ToString(provider, format); - } - - /// - /// Get string representation of value and unit. - /// - /// String format to use. Default: "{0:0.##} {1} for value and unit abbreviation respectively." - /// Arguments for string format. Value and unit are implictly included as arguments 0 and 1. - /// String representation. - /// Format to use for localization and number formatting. Defaults to if null. - [Obsolete("This method is deprecated and will be removed at a future release. Please use string.Format().")] - public string ToString([CanBeNull] IFormatProvider provider, [NotNull] string format, [NotNull] params object[] args) - { - if (format == null) throw new ArgumentNullException(nameof(format)); - if (args == null) throw new ArgumentNullException(nameof(args)); - - provider = provider ?? CultureInfo.CurrentUICulture; - - var value = Convert.ToDouble(Value); - var formatArgs = UnitFormatter.GetFormatArgs(Unit, value, provider, args); - return string.Format(provider, format, formatArgs); - } - - /// - /// - /// Gets the string representation of this instance in the specified format string using . - /// - /// The format string. - /// The string representation. - public string ToString(string format) - { - return ToString(format, CultureInfo.CurrentUICulture); - } - - /// - /// - /// Gets the string representation of this instance in the specified format string using the specified format provider, or if null. - /// - /// The format string. - /// Format to use for localization and number formatting. Defaults to if null. - /// The string representation. - public string ToString(string format, IFormatProvider formatProvider) - { - return QuantityFormatter.Format<$unitEnumName>(this, format, formatProvider); - } - - #endregion - - #region IConvertible Methods - - TypeCode IConvertible.GetTypeCode() - { - return TypeCode.Object; - } - - bool IConvertible.ToBoolean(IFormatProvider provider) - { - throw new InvalidCastException($"Converting {typeof($quantityName)} to bool is not supported."); - } - - byte IConvertible.ToByte(IFormatProvider provider) - { - return Convert.ToByte(_value); - } - - char IConvertible.ToChar(IFormatProvider provider) - { - throw new InvalidCastException($"Converting {typeof($quantityName)} to char is not supported."); - } - - DateTime IConvertible.ToDateTime(IFormatProvider provider) - { - throw new InvalidCastException($"Converting {typeof($quantityName)} to DateTime is not supported."); - } - - decimal IConvertible.ToDecimal(IFormatProvider provider) - { - return Convert.ToDecimal(_value); - } - - double IConvertible.ToDouble(IFormatProvider provider) - { - return Convert.ToDouble(_value); - } - - short IConvertible.ToInt16(IFormatProvider provider) - { - return Convert.ToInt16(_value); - } - - int IConvertible.ToInt32(IFormatProvider provider) - { - return Convert.ToInt32(_value); - } - - long IConvertible.ToInt64(IFormatProvider provider) - { - return Convert.ToInt64(_value); - } - - sbyte IConvertible.ToSByte(IFormatProvider provider) - { - return Convert.ToSByte(_value); - } - - float IConvertible.ToSingle(IFormatProvider provider) - { - return Convert.ToSingle(_value); - } - - string IConvertible.ToString(IFormatProvider provider) - { - return ToString("g", provider); - } - - object IConvertible.ToType(Type conversionType, IFormatProvider provider) - { - if(conversionType == typeof($quantityName)) - return this; - else if(conversionType == typeof($unitEnumName)) - return Unit; - else if(conversionType == typeof(QuantityType)) - return $quantityName.QuantityType; - else if(conversionType == typeof(BaseDimensions)) - return $quantityName.BaseDimensions; - else - throw new InvalidCastException($"Converting {typeof($quantityName)} to {conversionType} is not supported."); - } - - ushort IConvertible.ToUInt16(IFormatProvider provider) - { - return Convert.ToUInt16(_value); - } - - uint IConvertible.ToUInt32(IFormatProvider provider) - { - return Convert.ToUInt32(_value); - } - - ulong IConvertible.ToUInt64(IFormatProvider provider) - { - return Convert.ToUInt64(_value); - } - - #endregion - } -} -"@; -} - -function GenerateStaticProperties([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $baseUnitSingularName = $genArgs.BaseUnit.SingularName - $valueType = $genArgs.Quantity.BaseType -@" - - #region Static Properties - - /// - public static QuantityInfo<$unitEnumName> Info { get; } - - /// - /// The of this quantity. - /// - public static BaseDimensions BaseDimensions { get; } - - /// - /// The base unit of $quantityName, which is $baseUnitSingularName. All conversions go via this value. - /// - public static $unitEnumName BaseUnit { get; } = $unitEnumName.$baseUnitSingularName; - - /// - /// Represents the largest possible value of $quantityName - /// - public static $quantityName MaxValue { get; } = new $quantityName($valueType.MaxValue, BaseUnit); - - /// - /// Represents the smallest possible value of $quantityName - /// - public static $quantityName MinValue { get; } = new $quantityName($valueType.MinValue, BaseUnit); - - /// - /// The of this quantity. - /// - public static QuantityType QuantityType { get; } = QuantityType.$quantityName; - - /// - /// All units of measurement for the $quantityName quantity. - /// - public static $unitEnumName[] Units { get; } = Enum.GetValues(typeof($unitEnumName)).Cast<$unitEnumName>().Except(new $unitEnumName[]{ $unitEnumName.Undefined }).ToArray(); - - /// - /// Gets an instance of this quantity with a value of 0 in the base unit $baseUnitSingularName. - /// - public static $quantityName Zero { get; } = new $quantityName(0, BaseUnit); - - #endregion -"@; -} - -function GenerateProperties([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $valueType = $genArgs.Quantity.BaseType - [bool]$isDoubleValueType = $valueType -eq "double" -@" - - #region Properties - - /// - /// The numeric value this quantity was constructed with. - /// - public $valueType Value => _value; - -"@; if (-not $isDoubleValueType) { @" - double IQuantity.Value => (double) _value; - -"@; } @" - Enum IQuantity.Unit => Unit; - - /// - public $unitEnumName Unit => _unit.GetValueOrDefault(BaseUnit); - - /// - public QuantityInfo<$unitEnumName> QuantityInfo => Info; - - /// - QuantityInfo IQuantity.QuantityInfo => Info; - - /// - /// The of this quantity. - /// - public QuantityType Type => $quantityName.QuantityType; - - /// - /// The of this quantity. - /// - public BaseDimensions Dimensions => $quantityName.BaseDimensions; - - #endregion -"@; -} - -function GenerateConversionProperties([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $units = $genArgs.Quantity.Units -@" - - #region Conversion Properties -"@; - foreach ($unit in $units) { - $propertyName = $unit.PluralName; - $obsoleteAttribute = GetObsoleteAttribute($unit); - if ($obsoleteAttribute) - { - $obsoleteAttribute = "`r`n " + $obsoleteAttribute; # apply padding to conformance with code format in this page - } -@" - - /// - /// Get $quantityName in $propertyName. - /// $($obsoleteAttribute) - public double $propertyName => As($unitEnumName.$($unit.SingularName)); -"@; } -@" - - #endregion -"@; -} - -function GenerateStaticMethods([GeneratorArgs]$genArgs) -{ - $unitEnumName = $genArgs.UnitEnumName -@" - - #region Static Methods - - /// - /// Get unit abbreviation string. - /// - /// Unit to get abbreviation for. - /// Unit abbreviation string. - public static string GetAbbreviation($unitEnumName unit) - { - return GetAbbreviation(unit, null); - } - - /// - /// Get unit abbreviation string. - /// - /// Unit to get abbreviation for. - /// Unit abbreviation string. - /// Format to use for localization. Defaults to if null. - public static string GetAbbreviation($unitEnumName unit, [CanBeNull] IFormatProvider provider) - { - return UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit, provider); - } - - #endregion -"@; -} - -function GenerateStaticFactoryMethods([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $units = $genArgs.Quantity.Units - $valueType = $genArgs.Quantity.BaseType -@" - - #region Static Factory Methods - -"@; foreach ($unit in $units) { - $valueParamName = $unit.PluralName.ToLowerInvariant(); - $obsoleteAttribute = GetObsoleteAttribute($unit); - if ($obsoleteAttribute) - { - $obsoleteAttribute = "`r`n " + $obsoleteAttribute; # apply padding to conformance with code format in this page - } - @" - /// - /// Get $quantityName from $($unit.PluralName). - /// $($obsoleteAttribute) - /// If value is NaN or Infinity. - public static $quantityName From$($unit.PluralName)(QuantityValue $valueParamName) - { - $valueType value = ($valueType) $valueParamName; - return new $quantityName(value, $unitEnumName.$($unit.SingularName)); - } -"@; }@" - - /// - /// Dynamically convert from value and unit enum to . - /// - /// Value to convert from. - /// Unit to convert from. - /// $quantityName unit value. - public static $quantityName From(QuantityValue value, $unitEnumName fromUnit) - { - return new $quantityName(($valueType)value, fromUnit); - } - - #endregion -"@; - -} - -function GenerateStaticParseMethods([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $baseUnitPluralName = $genArgs.BaseUnit.PluralName - $units = $genArgs.Quantity.Units - $valueType = $genArgs.Quantity.BaseType -@" - - #region Static Parse Methods - - /// - /// Parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// - /// Expected string to have one or two pairs of quantity and unit in the format - /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" - /// - /// - /// More than one unit is represented by the specified unit abbreviation. - /// Example: Volume.Parse("1 cup") will throw, because it can refer to any of - /// , and . - /// - /// - /// If anything else goes wrong, typically due to a bug or unhandled case. - /// We wrap exceptions in to allow you to distinguish - /// Units.NET exceptions from other exceptions. - /// - public static $quantityName Parse(string str) - { - return Parse(str, null); - } - - /// - /// Parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// - /// Expected string to have one or two pairs of quantity and unit in the format - /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" - /// - /// - /// More than one unit is represented by the specified unit abbreviation. - /// Example: Volume.Parse("1 cup") will throw, because it can refer to any of - /// , and . - /// - /// - /// If anything else goes wrong, typically due to a bug or unhandled case. - /// We wrap exceptions in to allow you to distinguish - /// Units.NET exceptions from other exceptions. - /// - /// Format to use when parsing number and unit. Defaults to if null. - public static $quantityName Parse(string str, [CanBeNull] IFormatProvider provider) - { - return QuantityParser.Default.Parse<$quantityName, $unitEnumName>( - str, - provider, - From); - } - - /// - /// Try to parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// Resulting unit quantity if successful. - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - public static bool TryParse([CanBeNull] string str, out $quantityName result) - { - return TryParse(str, null, out result); - } - - /// - /// Try to parse a string with one or two quantities of the format "<quantity> <unit>". - /// - /// String to parse. Typically in the form: {number} {unit} - /// Resulting unit quantity if successful. - /// True if successful, otherwise false. - /// - /// Length.Parse("5.5 m", new CultureInfo("en-US")); - /// - /// Format to use when parsing number and unit. Defaults to if null. - public static bool TryParse([CanBeNull] string str, [CanBeNull] IFormatProvider provider, out $quantityName result) - { - return QuantityParser.Default.TryParse<$quantityName, $unitEnumName>( - str, - provider, - From, - out result); - } - - /// - /// Parse a unit string. - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.ParseUnit("m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// Error parsing string. - public static $unitEnumName ParseUnit(string str) - { - return ParseUnit(str, null); - } - - /// - /// Parse a unit string. - /// - /// String to parse. Typically in the form: {number} {unit} - /// - /// Length.ParseUnit("m", new CultureInfo("en-US")); - /// - /// The value of 'str' cannot be null. - /// Error parsing string. - /// Format to use when parsing number and unit. Defaults to if null. - public static $unitEnumName ParseUnit(string str, IFormatProvider provider = null) - { - return UnitParser.Default.Parse<$unitEnumName>(str, provider); - } - - /// - public static bool TryParseUnit(string str, out $unitEnumName unit) - { - return TryParseUnit(str, null, out unit); - } - - /// - /// Parse a unit string. - /// - /// String to parse. Typically in the form: {number} {unit} - /// The parsed unit if successful. - /// True if successful, otherwise false. - /// - /// Length.TryParseUnit("m", new CultureInfo("en-US")); - /// - /// Format to use when parsing number and unit. Defaults to if null. - public static bool TryParseUnit(string str, IFormatProvider provider, out $unitEnumName unit) - { - return UnitParser.Default.TryParse<$unitEnumName>(str, provider, out unit); - } - - #endregion -"@; -} - -function GenerateLogarithmicArithmeticOperators([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $valueType = $genArgs.Quantity.BaseType - $scalingFactor = $genArgs.Quantity.LogarithmicScalingFactor - # Most logarithmic operators need a simple scaling factor of 10. However, certain units such as voltage ratio need to use 20 instead. - $x = 10 * $scalingFactor; - @" - - #region Logarithmic Arithmetic Operators - - /// Negate the value. - public static $quantityName operator -($quantityName right) - { - return new $quantityName(-right.Value, right.Unit); - } - - /// Get from logarithmic addition of two . - public static $quantityName operator +($quantityName left, $quantityName right) - { - // Logarithmic addition - // Formula: $x*log10(10^(x/$x) + 10^(y/$x)) - return new $quantityName($x*Math.Log10(Math.Pow(10, left.Value/$x) + Math.Pow(10, right.GetValueAs(left.Unit)/$x)), left.Unit); - } - - /// Get from logarithmic subtraction of two . - public static $quantityName operator -($quantityName left, $quantityName right) - { - // Logarithmic subtraction - // Formula: $x*log10(10^(x/$x) - 10^(y/$x)) - return new $quantityName($x*Math.Log10(Math.Pow(10, left.Value/$x) - Math.Pow(10, right.GetValueAs(left.Unit)/$x)), left.Unit); - } - - /// Get from logarithmic multiplication of value and . - public static $quantityName operator *($valueType left, $quantityName right) - { - // Logarithmic multiplication = addition - return new $quantityName(left + right.Value, right.Unit); - } - - /// Get from logarithmic multiplication of value and . - public static $quantityName operator *($quantityName left, double right) - { - // Logarithmic multiplication = addition - return new $quantityName(left.Value + ($valueType)right, left.Unit); - } - - /// Get from logarithmic division of by value. - public static $quantityName operator /($quantityName left, double right) - { - // Logarithmic division = subtraction - return new $quantityName(left.Value - ($valueType)right, left.Unit); - } - - /// Get ratio value from logarithmic division of by . - public static double operator /($quantityName left, $quantityName right) - { - // Logarithmic division = subtraction - return Convert.ToDouble(left.Value - right.GetValueAs(left.Unit)); - } - - #endregion -"@; -} - -function GenerateArithmeticOperators([GeneratorArgs]$genArgs) -{ - if (-not $quantity.GenerateArithmetic) { return } - - # Logarithmic units required different arithmetic - if ($quantity.Logarithmic) { - GenerateLogarithmicArithmeticOperators $genArgs - return - } - - $quantityName = $genArgs.Quantity.Name - $baseUnitPluralName = $genArgs.BaseUnit.PluralName - $valueType = $genArgs.Quantity.BaseType - @" - - #region Arithmetic Operators - - /// Negate the value. - public static $quantityName operator -($quantityName right) - { - return new $quantityName(-right.Value, right.Unit); - } - - /// Get from adding two . - public static $quantityName operator +($quantityName left, $quantityName right) - { - return new $quantityName(left.Value + right.GetValueAs(left.Unit), left.Unit); - } - - /// Get from subtracting two . - public static $quantityName operator -($quantityName left, $quantityName right) - { - return new $quantityName(left.Value - right.GetValueAs(left.Unit), left.Unit); - } - - /// Get from multiplying value and . - public static $quantityName operator *($valueType left, $quantityName right) - { - return new $quantityName(left * right.Value, right.Unit); - } - - /// Get from multiplying value and . - public static $quantityName operator *($quantityName left, $valueType right) - { - return new $quantityName(left.Value * right, left.Unit); - } - - /// Get from dividing by value. - public static $quantityName operator /($quantityName left, $valueType right) - { - return new $quantityName(left.Value / right, left.Unit); - } - - /// Get ratio value from dividing by . - public static double operator /($quantityName left, $quantityName right) - { - return left.$baseUnitPluralName / right.$baseUnitPluralName; - } - - #endregion -"@; -} - -function GenerateEqualityAndComparison([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name -@" - - #region Equality / IComparable - - /// Returns true if less or equal to. - public static bool operator <=($quantityName left, $quantityName right) - { - return left.Value <= right.GetValueAs(left.Unit); - } - - /// Returns true if greater than or equal to. - public static bool operator >=($quantityName left, $quantityName right) - { - return left.Value >= right.GetValueAs(left.Unit); - } - - /// Returns true if less than. - public static bool operator <($quantityName left, $quantityName right) - { - return left.Value < right.GetValueAs(left.Unit); - } - - /// Returns true if greater than. - public static bool operator >($quantityName left, $quantityName right) - { - return left.Value > right.GetValueAs(left.Unit); - } - - /// Returns true if exactly equal. - /// Consider using for safely comparing floating point values. - public static bool operator ==($quantityName left, $quantityName right) - { - return left.Equals(right); - } - - /// Returns true if not exactly equal. - /// Consider using for safely comparing floating point values. - public static bool operator !=($quantityName left, $quantityName right) - { - return !(left == right); - } - - /// - public int CompareTo(object obj) - { - if(obj is null) throw new ArgumentNullException(nameof(obj)); - if(!(obj is $quantityName obj$quantityName)) throw new ArgumentException("Expected type $quantityName.", nameof(obj)); - - return CompareTo(obj$quantityName); - } - - /// - public int CompareTo($quantityName other) - { - return _value.CompareTo(other.GetValueAs(this.Unit)); - } - - /// - /// Consider using for safely comparing floating point values. - public override bool Equals(object obj) - { - if(obj is null || !(obj is $quantityName obj$quantityName)) - return false; - - return Equals(obj$quantityName); - } - - /// - /// Consider using for safely comparing floating point values. - public bool Equals($quantityName other) - { - return _value.Equals(other.GetValueAs(this.Unit)); - } - - /// - /// - /// Compare equality to another $quantityName within the given absolute or relative tolerance. - /// - /// - /// Relative tolerance is defined as the maximum allowable absolute difference between this quantity's value and - /// as a percentage of this quantity's value. will be converted into - /// this quantity's unit for comparison. A relative tolerance of 0.01 means the absolute difference must be within +/- 1% of - /// this quantity's value to be considered equal. - /// - /// In this example, the two quantities will be equal if the value of b is within +/- 1% of a (0.02m or 2cm). - /// - /// var a = Length.FromMeters(2.0); - /// var b = Length.FromInches(50.0); - /// a.Equals(b, 0.01, ComparisonType.Relative); - /// - /// - /// - /// - /// Absolute tolerance is defined as the maximum allowable absolute difference between this quantity's value and - /// as a fixed number in this quantity's unit. will be converted into - /// this quantity's unit for comparison. - /// - /// In this example, the two quantities will be equal if the value of b is within 0.01 of a (0.01m or 1cm). - /// - /// var a = Length.FromMeters(2.0); - /// var b = Length.FromInches(50.0); - /// a.Equals(b, 0.01, ComparisonType.Absolute); - /// - /// - /// - /// - /// Note that it is advised against specifying zero difference, due to the nature - /// of floating point operations and using System.Double internally. - /// - /// - /// The other quantity to compare to. - /// The absolute or relative tolerance value. Must be greater than or equal to 0. - /// The comparison type: either relative or absolute. - /// True if the absolute difference between the two values is not greater than the specified relative or absolute tolerance. - public bool Equals($quantityName other, double tolerance, ComparisonType comparisonType) - { - if(tolerance < 0) - throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be greater than or equal to 0."); - - double thisValue = (double)this.Value; - double otherValueInThisUnits = other.As(this.Unit); - - return UnitsNet.Comparison.Equals(thisValue, otherValueInThisUnits, tolerance, comparisonType); - } - - /// - /// Returns the hash code for this instance. - /// - /// A hash code for the current $quantityName. - public override int GetHashCode() - { - return new { QuantityType, Value, Unit }.GetHashCode(); - } - - #endregion -"@; -} - -function GenerateConversionMethods([GeneratorArgs]$genArgs) -{ - $quantityName = $genArgs.Quantity.Name - $unitEnumName = $genArgs.UnitEnumName - $valueType = $genArgs.Quantity.BaseType -@" - - #region Conversion Methods - - /// - /// Convert to the unit representation . - /// - /// Value converted to the specified unit. - public double As($unitEnumName unit) - { - if(Unit == unit) - return Convert.ToDouble(Value); - - var converted = GetValueAs(unit); - return Convert.ToDouble(converted); - } - - /// - public double As(UnitSystem unitSystem) - { - if(unitSystem == null) - throw new ArgumentNullException(nameof(unitSystem)); - - var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits); - - var firstUnitInfo = unitInfos.FirstOrDefault(); - if(firstUnitInfo == null) - throw new ArgumentException("No units were found for the given UnitSystem.", nameof(unitSystem)); - - return As(firstUnitInfo.Value); - } - - /// - double IQuantity.As(Enum unit) - { - if(!(unit is $unitEnumName unitAs$unitEnumName)) - throw new ArgumentException($"The given unit is of type {unit.GetType()}. Only {typeof($unitEnumName)} is supported.", nameof(unit)); - - return As(unitAs$unitEnumName); - } - - /// - /// Converts this $quantityName to another $quantityName with the unit representation . - /// - /// A $quantityName with the specified unit. - public $quantityName ToUnit($unitEnumName unit) - { - var convertedValue = GetValueAs(unit); - return new $quantityName(convertedValue, unit); - } - - /// - IQuantity IQuantity.ToUnit(Enum unit) - { - if(!(unit is $unitEnumName unitAs$unitEnumName)) - throw new ArgumentException($"The given unit is of type {unit.GetType()}. Only {typeof($unitEnumName)} is supported.", nameof(unit)); - - return ToUnit(unitAs$unitEnumName); - } - - /// - public $quantityName ToUnit(UnitSystem unitSystem) - { - if(unitSystem == null) - throw new ArgumentNullException(nameof(unitSystem)); - - var unitInfos = Info.GetUnitInfosFor(unitSystem.BaseUnits); - - var firstUnitInfo = unitInfos.FirstOrDefault(); - if(firstUnitInfo == null) - throw new ArgumentException("No units were found for the given UnitSystem.", nameof(unitSystem)); - - return ToUnit(firstUnitInfo.Value); - } - - /// - IQuantity IQuantity.ToUnit(UnitSystem unitSystem) => ToUnit(unitSystem); - - /// - IQuantity<$unitEnumName> IQuantity<$unitEnumName>.ToUnit($unitEnumName unit) => ToUnit(unit); - - /// - IQuantity<$unitEnumName> IQuantity<$unitEnumName>.ToUnit(UnitSystem unitSystem) => ToUnit(unitSystem); - - /// - /// Converts the current value + unit to the base unit. - /// This is typically the first step in converting from one unit to another. - /// - /// The value in the base unit representation. - private $valueType GetValueInBaseUnit() - { - switch(Unit) - { -"@; foreach ($unit in $units) { - $func = $unit.FromUnitToBaseFunc.Replace("x", "_value");@" - case $unitEnumName.$($unit.SingularName): return $func; -"@; }@" - default: - throw new NotImplementedException($"Can not convert {Unit} to base units."); - } - } - - private $valueType GetValueAs($unitEnumName unit) - { - if(Unit == unit) - return _value; - - var baseUnitValue = GetValueInBaseUnit(); - - switch(unit) - { -"@; foreach ($unit in $units) { - $func = $unit.FromBaseToUnitFunc.Replace("x", "baseUnitValue");@" - case $unitEnumName.$($unit.SingularName): return $func; -"@; }@" - default: - throw new NotImplementedException($"Can not convert {Unit} to {unit}."); - } - } - - #endregion -"@; -} diff --git a/UnitsNet/Scripts/Include-GenerateQuantityTypeSourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateQuantityTypeSourceCode.ps1 deleted file mode 100644 index b2364d3118..0000000000 --- a/UnitsNet/Scripts/Include-GenerateQuantityTypeSourceCode.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -function GenerateQuantityTypeSourceCode($quantities) -{ -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -// ReSharper disable once CheckNamespace -namespace UnitsNet -{ - /// - /// Lists all generated quantities with the same name as the quantity struct type, - /// such as Length, Mass, Force etc. - /// This is useful for populating options in the UI, such as creating a generic conversion - /// tool with inputValue, quantityName, fromUnit and toUnit selectors. - /// - public enum QuantityType - { -// Missing XML comment for public type or member -#pragma warning disable CS1591 - Undefined = 0, -"@; foreach ($quantity in $quantities) { -@" - $($quantity.Name), -"@; }@" -// Missing XML comment for public type or member -#pragma warning restore CS1591 - } -} -"@; -} diff --git a/UnitsNet/Scripts/Include-GenerateStaticQuantitySourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateStaticQuantitySourceCode.ps1 deleted file mode 100644 index d7a52cd54b..0000000000 --- a/UnitsNet/Scripts/Include-GenerateStaticQuantitySourceCode.ps1 +++ /dev/null @@ -1,120 +0,0 @@ -using module ".\Types.psm1" - -function GenerateStaticQuantitySourceCode([Quantity[]]$quantities, [string]$target) -{ -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using System.Globalization; -using JetBrains.Annotations; -using UnitsNet.InternalHelpers; -using UnitsNet.Units; - -namespace UnitsNet -{ - /// - /// Dynamically parse or construct quantities when types are only known at runtime. - /// - public static partial class Quantity - { - /// - /// Dynamically constructs a quantity of the given with the value in the quantity's base units. - /// - /// The of the quantity to create. - /// The value to construct the quantity with. - /// The created quantity. - public static IQuantity FromQuantityType(QuantityType quantityType, QuantityValue value) - { - switch(quantityType) - { -"@; foreach ($quantity in $quantities) { - $quantityName = $quantity.Name;@" - case QuantityType.$quantityName`: - return $quantityName.From(value, $quantityName.BaseUnit); -"@; }@" - default: - throw new ArgumentException($"{quantityType} is not a supported quantity type."); - } - } - - /// - /// Try to dynamically construct a quantity. - /// - /// Numeric value. - /// Unit enum value. - /// The resulting quantity if successful, otherwise default. - /// True if successful with assigned the value, otherwise false. - public static bool TryFrom(QuantityValue value, Enum unit, out IQuantity quantity) - { - switch (unit) - { -"@; foreach ($quantity in $quantities) { - $quantityName = $quantity.Name - $unitTypeName = $quantityName + "Unit" - $unitValue = toCamelCase($unitTypeName);@" - case $unitTypeName $($unitValue): - quantity = $quantityName.From(value, $unitValue); - return true; -"@; }@" - default: - { - quantity = default(IQuantity); - return false; - } - } - } - - /// - /// Try to dynamically parse a quantity string representation. - /// - /// The format provider to use for lookup. Defaults to if null. - /// Type of quantity, such as . - /// Quantity string representation, such as "1.5 kg". Must be compatible with given quantity type. - /// The resulting quantity if successful, otherwise default. - /// The parsed quantity. - public static bool TryParse([CanBeNull] IFormatProvider formatProvider, Type quantityType, string quantityString, out IQuantity quantity) - { - quantity = default(IQuantity); - - if (!typeof(IQuantity).Wrap().IsAssignableFrom(quantityType)) - return false; - - var parser = QuantityParser.Default; - - switch(quantityType) - { -"@; foreach ($quantity in $quantities) { - $quantityName = $quantity.Name;@" - case Type _ when quantityType == typeof($quantityName): - return parser.TryParse<$quantityName, $($quantityName)Unit>(quantityString, formatProvider, $quantityName.From, out quantity); -"@; }@" - default: - return false; - } - } - } -} -"@; -} - -function toCamelCase([string] $str) { - return [char]::ToLower($str[0]) + $str.Substring(1) -} diff --git a/UnitsNet/Scripts/Include-GenerateTemplates.ps1 b/UnitsNet/Scripts/Include-GenerateTemplates.ps1 deleted file mode 100644 index f6b2d15ce7..0000000000 --- a/UnitsNet/Scripts/Include-GenerateTemplates.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -<# -.SYNOPSIS -Returns the Obsolete attribute if ObsoleteText has been defined on the JSON input - otherwise returns empty string -It is up to the consumer to wrap any padding/new lines in order to keep to correct indentation formats -#> -function GetObsoleteAttribute($quantityOrUnit) -{ - if ($quantityOrUnit.ObsoleteText) - { - return "[System.Obsolete(""$($quantityOrUnit.ObsoleteText)"")]"; - } - return ""; -} diff --git a/UnitsNet/Scripts/Include-GenerateUnitSystemDefaultSourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateUnitSystemDefaultSourceCode.ps1 deleted file mode 100644 index e2fea2f963..0000000000 --- a/UnitsNet/Scripts/Include-GenerateUnitSystemDefaultSourceCode.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -function GenerateUnitSystemDefaultSourceCode($quantities) -{ -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using UnitsNet.Units; - -// ReSharper disable RedundantCommaInArrayInitializer -// ReSharper disable once CheckNamespace -namespace UnitsNet -{ - public partial class UnitAbbreviationsCache - { - private static readonly (string CultureName, Type UnitType, int UnitValue, string[] UnitAbbreviations)[] GeneratedLocalizations - = new [] - { -"@; - foreach ($quantity in $quantities) - { - $quantityName = $quantity.Name; - $unitEnumName = "$quantityName" + "Unit"; - - foreach ($unit in $quantity.Units) - { - $enumValue = $unit.SingularName; - - foreach ($localization in $unit.Localization) - { - $cultureName = $localization.Culture; - $abbreviationParams = $localization.Abbreviations -join '", "' -@" - (`"$cultureName`", typeof($unitEnumName), (int)$unitEnumName.$enumValue, new string[]{`"$abbreviationParams`"}), -"@; - } - } - } -@" - }; - } -} -"@; -} diff --git a/UnitsNet/Scripts/Include-GenerateUnitTestBaseClassSourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateUnitTestBaseClassSourceCode.ps1 deleted file mode 100644 index 71a80f1db7..0000000000 --- a/UnitsNet/Scripts/Include-GenerateUnitTestBaseClassSourceCode.ps1 +++ /dev/null @@ -1,300 +0,0 @@ -using module ".\Types.psm1" -function GenerateUnitTestBaseClassSourceCode([Quantity]$quantity) -{ - $quantityName = $quantity.Name - $units = $quantity.Units - $valueType = $quantity.BaseType - [Unit]$baseUnit = $units | where { $_.SingularName -eq $quantity.BaseUnit } | Select-Object -First 1 - $baseUnitSingularName = $baseUnit.SingularName - $baseUnitPluralName = $baseUnit.PluralName - $baseUnitVariableName = $baseUnitSingularName.ToLowerInvariant() - $unitEnumName = "$quantityName" + "Unit" - -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; -using System.Linq; -using UnitsNet.Units; -using Xunit; - -// Disable build warning CS1718: Comparison made to same variable; did you mean to compare something else? -#pragma warning disable 1718 - -// ReSharper disable once CheckNamespace -namespace UnitsNet.Tests -{ - /// - /// Test of $quantityName. - /// -// ReSharper disable once PartialTypeWithSinglePart - public abstract partial class $($quantityName)TestsBase - { -"@; foreach ($unit in $units) {@" - protected abstract double $($unit.PluralName)InOne$($baseUnit.SingularName) { get; } -"@; }@" - -// ReSharper disable VirtualMemberNeverOverriden.Global -"@; foreach ($unit in $units) {@" - protected virtual double $($unit.PluralName)Tolerance { get { return 1e-5; } } -"@; }@" -// ReSharper restore VirtualMemberNeverOverriden.Global - - [Fact] - public void Ctor_WithUndefinedUnit_ThrowsArgumentException() - { - Assert.Throws(() => new $quantityName(($valueType)0.0, $unitEnumName.Undefined)); - } - -"@; if ($quantity.BaseType -eq "double") {@" - [Fact] - public void Ctor_WithInfinityValue_ThrowsArgumentException() - { - Assert.Throws(() => new $quantityName(double.PositiveInfinity, $unitEnumName.$($baseUnit.SingularName))); - Assert.Throws(() => new $quantityName(double.NegativeInfinity, $unitEnumName.$($baseUnit.SingularName))); - } - - [Fact] - public void Ctor_WithNaNValue_ThrowsArgumentException() - { - Assert.Throws(() => new $quantityName(double.NaN, $unitEnumName.$($baseUnit.SingularName))); - } -"@; }@" - - [Fact] - public void $($baseUnit.SingularName)To$($quantityName)Units() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance($($unit.PluralName)InOne$($baseUnit.SingularName), $baseUnitVariableName.$($unit.PluralName), $($unit.PluralName)Tolerance); -"@; }@" - } - - [Fact] - public void FromValueAndUnit() - { -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance(1, $quantityName.From(1, $unitEnumName.$($unit.SingularName)).$($unit.PluralName), $($unit.PluralName)Tolerance); -"@; }@" - } - -"@; if ($quantity.BaseType -eq "double") {@" - [Fact] - public void From$($baseUnit.PluralName)_WithInfinityValue_ThrowsArgumentException() - { - Assert.Throws(() => $quantityName.From$($baseUnit.PluralName)(double.PositiveInfinity)); - Assert.Throws(() => $quantityName.From$($baseUnit.PluralName)(double.NegativeInfinity)); - } - - [Fact] - public void From$($baseUnit.PluralName)_WithNanValue_ThrowsArgumentException() - { - Assert.Throws(() => $quantityName.From$($baseUnit.PluralName)(double.NaN)); - } -"@; }@" - - [Fact] - public void As() - { - var $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance($($unit.PluralName)InOne$($baseUnit.SingularName), $baseUnitVariableName.As($($quantityName)Unit.$($unit.SingularName)), $($unit.PluralName)Tolerance); -"@; }@" - } - - [Fact] - public void ToUnit() - { - var $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) -{ - $asQuantityVariableName = "$($unit.SingularName.ToLowerInvariant())Quantity"; -@" - - var $asQuantityVariableName = $baseUnitVariableName.ToUnit($($quantityName)Unit.$($unit.SingularName)); - AssertEx.EqualTolerance($($unit.PluralName)InOne$($baseUnit.SingularName), (double)$asQuantityVariableName.Value, $($unit.PluralName)Tolerance); - Assert.Equal($($quantityName)Unit.$($unit.SingularName), $asQuantityVariableName.Unit); -"@; }@" - } - - [Fact] - public void ConversionRoundTrip() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); -"@; foreach ($unit in $units) {@" - AssertEx.EqualTolerance(1, $quantityName.From$($unit.PluralName)($baseUnitVariableName.$($unit.PluralName)).$baseUnitPluralName, $($unit.PluralName)Tolerance); -"@; }@" - } - -"@; if ($quantity.Logarithmic -eq $true) {@" - [Fact] - public void LogarithmicArithmeticOperators() - { - $quantityName v = $quantityName.From$baseUnitPluralName(40); - AssertEx.EqualTolerance(-40, -v.$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertLogarithmicAddition(); - AssertLogarithmicSubtraction(); - AssertEx.EqualTolerance(50, (v*10).$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertEx.EqualTolerance(50, (10*v).$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertEx.EqualTolerance(35, (v/5).$baseUnitPluralName, $($unit.PluralName)Tolerance); - AssertEx.EqualTolerance(35, v/$quantityName.From$baseUnitPluralName(5), $($unit.PluralName)Tolerance); - } - - protected abstract void AssertLogarithmicAddition(); - - protected abstract void AssertLogarithmicSubtraction(); - -"@; } - elseif ($quantity.GenerateArithmetic -eq $true) {@" - [Fact] - public void ArithmeticOperators() - { - $quantityName v = $quantityName.From$baseUnitPluralName(1); - AssertEx.EqualTolerance(-1, -v.$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, ($quantityName.From$baseUnitPluralName(3)-v).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, (v + v).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(10, (v*10).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(10, (10*v).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, ($quantityName.From$baseUnitPluralName(10)/5).$baseUnitPluralName, $($baseUnit.PluralName)Tolerance); - AssertEx.EqualTolerance(2, $quantityName.From$baseUnitPluralName(10)/$quantityName.From$baseUnitPluralName(5), $($baseUnit.PluralName)Tolerance); - } -"@; }@" - - [Fact] - public void ComparisonOperators() - { - $quantityName one$($baseUnit.SingularName) = $quantityName.From$baseUnitPluralName(1); - $quantityName two$baseUnitPluralName = $quantityName.From$baseUnitPluralName(2); - - Assert.True(one$($baseUnit.SingularName) < two$baseUnitPluralName); - Assert.True(one$($baseUnit.SingularName) <= two$baseUnitPluralName); - Assert.True(two$baseUnitPluralName > one$($baseUnit.SingularName)); - Assert.True(two$baseUnitPluralName >= one$($baseUnit.SingularName)); - - Assert.False(one$($baseUnit.SingularName) > two$baseUnitPluralName); - Assert.False(one$($baseUnit.SingularName) >= two$baseUnitPluralName); - Assert.False(two$baseUnitPluralName < one$($baseUnit.SingularName)); - Assert.False(two$baseUnitPluralName <= one$($baseUnit.SingularName)); - } - - [Fact] - public void CompareToIsImplemented() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.Equal(0, $baseUnitVariableName.CompareTo($baseUnitVariableName)); - Assert.True($baseUnitVariableName.CompareTo($quantityName.Zero) > 0); - Assert.True($quantityName.Zero.CompareTo($baseUnitVariableName) < 0); - } - - [Fact] - public void CompareToThrowsOnTypeMismatch() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.Throws(() => $baseUnitVariableName.CompareTo(new object())); - } - - [Fact] - public void CompareToThrowsOnNull() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.Throws(() => $baseUnitVariableName.CompareTo(null)); - } - - [Fact] - public void EqualityOperators() - { - var a = $quantityName.From$baseUnitPluralName(1); - var b = $quantityName.From$baseUnitPluralName(2); - - // ReSharper disable EqualExpressionComparison - - Assert.True(a == a); - Assert.False(a != a); - - Assert.True(a != b); - Assert.False(a == b); - - Assert.False(a == null); - Assert.False(null == a); - -// ReSharper restore EqualExpressionComparison - } - - [Fact] - public void EqualsIsImplemented() - { - var a = $quantityName.From$baseUnitPluralName(1); - var b = $quantityName.From$baseUnitPluralName(2); - - Assert.True(a.Equals(a)); - Assert.False(a.Equals(b)); - Assert.False(a.Equals(null)); - } - - [Fact] - public void EqualsRelativeToleranceIsImplemented() - { - var v = $quantityName.From$baseUnitPluralName(1); - Assert.True(v.Equals($quantityName.From$baseUnitPluralName(1), $($baseUnitPluralName)Tolerance, ComparisonType.Relative)); - Assert.False(v.Equals($quantityName.Zero, $($baseUnitPluralName)Tolerance, ComparisonType.Relative)); - } - - [Fact] - public void EqualsReturnsFalseOnTypeMismatch() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.False($baseUnitVariableName.Equals(new object())); - } - - [Fact] - public void EqualsReturnsFalseOnNull() - { - $quantityName $baseUnitVariableName = $quantityName.From$baseUnitPluralName(1); - Assert.False($baseUnitVariableName.Equals(null)); - } - - [Fact] - public void UnitsDoesNotContainUndefined() - { - Assert.DoesNotContain($unitEnumName.Undefined, $quantityName.Units); - } - - [Fact] - public void HasAtLeastOneAbbreviationSpecified() - { - var units = Enum.GetValues(typeof($unitEnumName)).Cast<$unitEnumName>(); - foreach(var unit in units) - { - if(unit == $unitEnumName.Undefined) - continue; - - var defaultAbbreviation = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit); - } - } - - [Fact] - public void BaseDimensionsShouldNeverBeNull() - { - Assert.False($quantityName.BaseDimensions is null); - } - } -} -"@; -} diff --git a/UnitsNet/Scripts/Include-GenerateUnitTestPlaceholderSourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateUnitTestPlaceholderSourceCode.ps1 deleted file mode 100644 index 055b2bfc8e..0000000000 --- a/UnitsNet/Scripts/Include-GenerateUnitTestPlaceholderSourceCode.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -function GenerateUnitTestPlaceholderSourceCode($quantity) -{ - $quantityName = $quantity.Name; -@" -//------------------------------------------------------------------------------ -// -// This code was generated (once) by \generate-code.bat, but will not be -// regenerated when it already exists. The purpose of creating this file is to make -// it easier to remember to implement all the unit conversion test cases. -// -// Whenever a new unit is added to this quantity and \generate-code.bat is run, -// the base test class will get a new abstract property and cause a compile error -// in this derived class, reminding the developer to implement the test case -// for the new unit. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -using System; - -namespace UnitsNet.Tests.CustomCode -{ - public class $($quantityName)Tests : $($quantityName)TestsBase - { - // Override properties in base class here - } -} -"@; -} diff --git a/UnitsNet/Scripts/Include-GenerateUnitTypeSourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateUnitTypeSourceCode.ps1 deleted file mode 100644 index 047f218560..0000000000 --- a/UnitsNet/Scripts/Include-GenerateUnitTypeSourceCode.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -function GenerateUnitTypeSourceCode($quantity) { - $quantityName = $quantity.Name; - $units = $quantity.Units; - $unitEnumName = "$($quantityName)Unit"; -@" -//------------------------------------------------------------------------------ -// -// This code was generated by \generate-code.bat. -// -// Changes to this file will be lost when the code is regenerated. -// The build server regenerates the code before each build and a pre-build -// step will regenerate the code on each local build. -// -// See https://github.com/angularsen/UnitsNet/wiki/Adding-a-New-Unit for how to add or edit units. -// -// Add CustomCode\Quantities\MyQuantity.extra.cs files to add code to generated quantities. -// Add UnitDefinitions\MyQuantity.json and run generate-code.bat to generate new units or quantities. -// -// -//------------------------------------------------------------------------------ - -// Licensed under MIT No Attribution, see LICENSE file at the root. -// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. - -// ReSharper disable once CheckNamespace -namespace UnitsNet.Units -{ - // Disable missing XML comment warnings for the generated unit enums. - #pragma warning disable 1591 - - public enum $unitEnumName - { - Undefined = 0, -"@; foreach ($unit in $units) { - $obsoleteAttribute = GetObsoleteAttribute($unit); - - if ($unit.XmlDocSummary) {@" - - /// - /// $($unit.XmlDocSummary) - /// -"@; } - if ($unit.XmlDocRemarks) {@" - /// $($unit.XmlDocRemarks) -"@; } - if ($obsoleteAttribute) {@" - $($obsoleteAttribute) -"@; }@" - $($unit.SingularName), -"@; }@" - } - - #pragma warning restore 1591 -} -"@; -} diff --git a/UnitsNet/Scripts/Types.psm1 b/UnitsNet/Scripts/Types.psm1 deleted file mode 100644 index c94c9c2caa..0000000000 --- a/UnitsNet/Scripts/Types.psm1 +++ /dev/null @@ -1,62 +0,0 @@ -class Quantity -{ - [string]$Name - [string]$XmlDocSummary - [string]$XmlDocRemarks - [string]$BaseUnit - [string]$BaseType # TODO Rename me to ValueType - [BaseDimensions]$BaseDimensions = [BaseDimensions]::new() - [boolean]$GenerateArithmetic = $true - [boolean]$Logarithmic = $false - [int]$LogarithmicScalingFactor = 1 - [Unit[]]$Units = @() -} - -class Unit -{ - [string]$SingularName - [string]$PluralName - [string]$XmlDocSummary - [string]$XmlDocRemarks - [BaseUnits]$BaseUnits - [string]$FromUnitToBaseFunc - [string]$FromBaseToUnitFunc - [string[]]$Prefixes = @() - [Localization[]]$Localization = @() -} - -class BaseUnits -{ - [string]$Length - [string]$Mass - [string]$Time - [string]$ElectricCurrent - [string]$Temperature - [string]$AmountOfSubstance - [string]$LuminousIntensity -} - -class Localization -{ - [string]$Culture - [string[]]$Abbreviations = @() - [object[]]$AbbreviationsWithPrefixes = @() -} - -class BaseDimensions -{ - [int]$Length = 0 - [int]$Mass = 0 - [int]$Time = 0 - [int]$ElectricCurrent = 0 - [int]$Temperature = 0 - [int]$AmountOfSubstance = 0 - [int]$LuminousIntensity = 0 -} - -class GeneratorArgs -{ - [Quantity]$Quantity - [Unit]$BaseUnit - [string]$UnitEnumName -} diff --git a/after.UnitsNet.sln.targets b/after.UnitsNet.sln.targets deleted file mode 100644 index 7af52e24a9..0000000000 --- a/after.UnitsNet.sln.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/generate-code.bat b/generate-code.bat index 226a73d0da..26f3ccc04a 100644 --- a/generate-code.bat +++ b/generate-code.bat @@ -1,4 +1,3 @@ @echo off SET scriptdir=%~dp0 -powershell -ExecutionPolicy Bypass -NoProfile -File "%scriptdir%UnitsNet/Scripts/GenerateUnits.ps1" -powershell -ExecutionPolicy Bypass -NoProfile -File "%scriptdir%UnitsNet.WindowsRuntimeComponent./Scripts/GenerateUnits.ps1" +dotnet run --project "%scriptdir%/CodeGen"