diff --git a/.editorconfig b/.editorconfig index f68d0e5c..a74b74d2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -263,4 +263,4 @@ dotnet_diagnostic.IDE0005.severity = warning # Enforce formatting https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#rule-id-ide0055-fix-formatting dotnet_diagnostic.IDE0055.severity = error dotnet_diagnostic.IDE1006.severity = error -dotnet_diagnostic.IDE0022.severity = error +dotnet_diagnostic.IDE0022.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index 50156e31..4a218b7a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,7 @@ true $(NoWarn);IDE0005 True - None + Recommended diff --git a/QRCoder/ASCIIQRCode.cs b/QRCoder/ASCIIQRCode.cs index 84ea3b99..dedadb08 100644 --- a/QRCoder/ASCIIQRCode.cs +++ b/QRCoder/ASCIIQRCode.cs @@ -31,7 +31,7 @@ public AsciiQRCode(QRCodeData data) : base(data) { } public string GetGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true, string endOfLine = "\n") { if (repeatPerModule < 1) - throw new Exception("The repeatPerModule-parameter must be 1 or greater."); + throw new ArgumentOutOfRangeException(nameof(repeatPerModule), "The repeatPerModule parameter must be 1 or greater."); return string.Join(endOfLine, GetLineByLineGraphic(repeatPerModule, darkColorString, whiteSpaceString, drawQuietZones)); } diff --git a/QRCoder/ArtQRCode.cs b/QRCoder/ArtQRCode.cs index 225b18eb..7ace0d9b 100644 --- a/QRCoder/ArtQRCode.cs +++ b/QRCoder/ArtQRCode.cs @@ -63,7 +63,7 @@ public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap? finderPatternImage = null) { if (pixelSizeFactor > 1) - throw new Exception("The parameter pixelSize must be between 0 and 1. (0-100%)"); + throw new ArgumentOutOfRangeException(nameof(pixelSizeFactor), "The parameter pixelSizeFactor must be between 0 and 1. (0-100%)"); int pixelSize = (int)Math.Min(pixelsPerModule, Math.Floor(pixelsPerModule * pixelSizeFactor)); var numModules = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8); @@ -134,7 +134,7 @@ public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, /// Size of the dots /// Color of the pixels /// - private Bitmap MakeDotPixel(int pixelsPerModule, int pixelSize, SolidBrush brush) + private static Bitmap MakeDotPixel(int pixelsPerModule, int pixelSize, SolidBrush brush) { // draw a dot var bitmap = new Bitmap(pixelSize, pixelSize); @@ -168,7 +168,7 @@ private Bitmap MakeDotPixel(int pixelsPerModule, int pixelSize, SolidBrush brush /// Y position /// Total number of modules per row /// true, if position is part of quiet zone - private bool IsPartOfQuietZone(int x, int y, int numModules) + private static bool IsPartOfQuietZone(int x, int y, int numModules) { return x < 4 || //left @@ -186,7 +186,7 @@ private bool IsPartOfQuietZone(int x, int y, int numModules) /// Total number of modules per row /// Offset in modules (usually depending on drawQuietZones parameter) /// true, if position is part of any finder pattern - private bool IsPartOfFinderPattern(int x, int y, int numModules, int offset) + private static bool IsPartOfFinderPattern(int x, int y, int numModules, int offset) { var cornerSize = 11 - offset; var outerLimitLow = (numModules - cornerSize - 1); @@ -204,7 +204,7 @@ private bool IsPartOfFinderPattern(int x, int y, int numModules, int offset) /// /// /// Resized image as bitmap - private Bitmap Resize(Bitmap image, int newSize) + private static Bitmap Resize(Bitmap image, int newSize) { float scale = Math.Min((float)newSize / image.Width, (float)newSize / image.Height); var scaledWidth = (int)(image.Width * scale); diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index b5e1cc3a..166cc49f 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -141,7 +141,7 @@ public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, #if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - private string BitmapToBase64(Bitmap bmp, ImageType imgType) + private static string BitmapToBase64(Bitmap bmp, ImageType imgType) { var iFormat = imgType switch { diff --git a/QRCoder/BitmapByteQRCode.cs b/QRCoder/BitmapByteQRCode.cs index 46d3d584..0ca6c5ed 100644 --- a/QRCoder/BitmapByteQRCode.cs +++ b/QRCoder/BitmapByteQRCode.cs @@ -41,7 +41,7 @@ public byte[] GetGraphic(int pixelsPerModule) /// The color of the light modules in HTML hex format. /// Returns the QR code graphic as a bitmap byte array. public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex) - => GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex)); + => GetGraphic(pixelsPerModule, darkColorHtmlHex.HexColorToByteArray(), lightColorHtmlHex.HexColorToByteArray()); /// /// Returns the QR code graphic as a bitmap byte array. @@ -134,29 +134,13 @@ public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgb, byte[] lightC } - /// - /// Converts a hex color string to a byte array. - /// - /// The hex color string to convert. - /// Returns the color as a byte array. - private byte[] HexColorToByteArray(string colorString) - { - if (colorString.StartsWith("#")) - colorString = colorString.Substring(1); - byte[] byteColor = new byte[colorString.Length / 2]; - for (int i = 0; i < byteColor.Length; i++) - byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture); - return byteColor; - } - - /// /// Converts an integer to a 4 bytes and writes them to a byte array at given position /// /// The integer to convert. /// Index of destinationArray where the converted bytes are written to /// Destination byte array that receives the bytes - private void CopyIntAs4ByteToArray(int inp, int destinationIndex, byte[] destinationArray) + private static void CopyIntAs4ByteToArray(int inp, int destinationIndex, byte[] destinationArray) { unchecked { diff --git a/QRCoder/Extensions/StringExtensions.cs b/QRCoder/Extensions/StringExtensions.cs index 702c074e..6dcbb292 100644 --- a/QRCoder/Extensions/StringExtensions.cs +++ b/QRCoder/Extensions/StringExtensions.cs @@ -29,4 +29,30 @@ public static bool IsNullOrWhiteSpace( return string.IsNullOrWhiteSpace(value); #endif } + + /// + /// Converts a hex color string to a byte array. + /// + /// Color in HEX format like #ffffff. + /// Returns the color as a byte array. + internal static byte[] HexColorToByteArray(this string colorString) + { + var offset = 0; + if (colorString.StartsWith("#", StringComparison.Ordinal)) + offset = 1; + byte[] byteColor = new byte[(colorString.Length - offset) / 2]; + for (int i = 0; i < byteColor.Length; i++) +#if HAS_SPAN + byteColor[i] = byte.Parse(colorString.AsSpan(i * 2 + offset, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); +#else + byteColor[i] = byte.Parse(colorString.Substring(i * 2 + offset, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); +#endif + return byteColor; + } + +#if NETSTANDARD1_3 + /// + internal static string ToString(this char c, CultureInfo _) + => c.ToString(); +#endif } diff --git a/QRCoder/Extensions/StringValueAttribute.cs b/QRCoder/Extensions/StringValueAttribute.cs index dcb9b731..ca93b888 100644 --- a/QRCoder/Extensions/StringValueAttribute.cs +++ b/QRCoder/Extensions/StringValueAttribute.cs @@ -7,6 +7,7 @@ namespace QRCoder.Extensions; /// Used to represent a string value for a value in an enum /// [Obsolete("This attribute will be removed in a future version of QRCoder.")] +[AttributeUsage(AttributeTargets.Field)] public class StringValueAttribute : Attribute { diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs index ce465401..003882ee 100644 --- a/QRCoder/PayloadGenerator.cs +++ b/QRCoder/PayloadGenerator.cs @@ -17,20 +17,24 @@ public static partial class PayloadGenerator private static bool IsValidIban(string iban) { //Clean IBAN - var ibanCleared = iban.ToUpper().Replace(" ", "").Replace("-", ""); + var ibanCleared = iban.ToUpperInvariant().Replace(" ", "").Replace("-", ""); //Check for general structure var structurallyValid = Regex.IsMatch(ibanCleared, @"^[a-zA-Z]{2}[0-9]{2}([a-zA-Z0-9]?){16,30}$"); //Check IBAN checksum var checksumValid = false; - var sum = $"{ibanCleared.Substring(4)}{ibanCleared.Substring(0, 4)}".ToCharArray().Aggregate("", (current, c) => current + (char.IsLetter(c) ? (c - 55).ToString() : c.ToString())); + var sum = $"{ibanCleared.Substring(4)}{ibanCleared.Substring(0, 4)}".ToCharArray().Aggregate("", (current, c) => current + (char.IsLetter(c) ? (c - 55).ToString(CultureInfo.InvariantCulture) : c.ToString(CultureInfo.InvariantCulture))); int m = 0; for (int i = 0; i < (int)Math.Ceiling((sum.Length - 2) / 7d); i++) { var offset = (i == 0 ? 0 : 2); var start = i * 7 + offset; +#if NET5_0_OR_GREATER + var n = string.Concat(i == 0 ? "" : m.ToString(CultureInfo.InvariantCulture), sum.AsSpan(start, Math.Min(9 - offset, sum.Length - start))); +#else var n = (i == 0 ? "" : m.ToString()) + sum.Substring(start, Math.Min(9 - offset, sum.Length - start)); +#endif if (!int.TryParse(n, NumberStyles.Any, CultureInfo.InvariantCulture, out m)) break; m %= 97; @@ -49,8 +53,8 @@ private static bool IsValidQRIban(string iban) var foundQrIid = false; try { - var ibanCleared = iban.ToUpper().Replace(" ", "").Replace("-", ""); - var possibleQrIid = Convert.ToInt32(ibanCleared.Substring(4, 5)); + var ibanCleared = iban.ToUpperInvariant().Replace(" ", "").Replace("-", ""); + var possibleQrIid = Convert.ToInt32(ibanCleared.Substring(4, 5), CultureInfo.InvariantCulture); foundQrIid = possibleQrIid >= 30000 && possibleQrIid <= 31999; } catch { } diff --git a/QRCoder/PayloadGenerator/BezahlCode.cs b/QRCoder/PayloadGenerator/BezahlCode.cs index 50b7354e..7f56bc2e 100644 --- a/QRCoder/PayloadGenerator/BezahlCode.cs +++ b/QRCoder/PayloadGenerator/BezahlCode.cs @@ -123,12 +123,12 @@ public BezahlCode(AuthorityType authority, string name, string account, string b } else if (internalMode == 2) { -#pragma warning disable CS0612 +#pragma warning disable CS0618 if (authority != AuthorityType.periodicsinglepayment && authority != AuthorityType.singledirectdebit && authority != AuthorityType.singlepayment) throw new BezahlCodeException("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); if (authority == AuthorityType.periodicsinglepayment && (string.IsNullOrEmpty(periodicTimeunit) || periodicTimeunitRotation == 0)) throw new BezahlCodeException("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); -#pragma warning restore CS0612 +#pragma warning restore CS0618 } else if (internalMode == 3) { @@ -155,17 +155,17 @@ public BezahlCode(AuthorityType authority, string name, string account, string b _reason = reason; //Non-SEPA payment types -#pragma warning disable CS0612 +#pragma warning disable CS0618 if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.singledirectdebit || authority == AuthorityType.singlepayment || authority == AuthorityType.contact || (authority == AuthorityType.contact_v2 && oldWayFilled)) { -#pragma warning restore CS0612 +#pragma warning restore CS0618 if (!Regex.IsMatch(account.Replace(" ", ""), @"^[0-9]{1,9}$")) throw new BezahlCodeException("The account entered isn't valid."); - _account = account.Replace(" ", "").ToUpper(); + _account = account.Replace(" ", "").ToUpperInvariant(); if (!Regex.IsMatch(bnc.Replace(" ", ""), @"^[0-9]{1,9}$")) throw new BezahlCodeException("The bnc entered isn't valid."); - _bnc = bnc.Replace(" ", "").ToUpper(); + _bnc = bnc.Replace(" ", "").ToUpperInvariant(); if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) { @@ -180,10 +180,10 @@ public BezahlCode(AuthorityType authority, string name, string account, string b { if (!IsValidIban(iban)) throw new BezahlCodeException("The IBAN entered isn't valid."); - _iban = iban.Replace(" ", "").ToUpper(); + _iban = iban.Replace(" ", "").ToUpperInvariant(); if (!IsValidBic(bic)) throw new BezahlCodeException("The BIC entered isn't valid."); - _bic = bic.Replace(" ", "").ToUpper(); + _bic = bic.Replace(" ", "").ToUpperInvariant(); if (authority != AuthorityType.contact_v2) { @@ -205,7 +205,7 @@ public BezahlCode(AuthorityType authority, string name, string account, string b //Checks for all payment types if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) { - if (amount.ToString().Replace(",", ".").Contains(".") && amount.ToString().Replace(",", ".").Split('.')[1].TrimEnd('0').Length > 2) + if (amount.ToString(CultureInfo.InvariantCulture).Contains('.') && amount.ToString(CultureInfo.InvariantCulture).Split('.')[1].TrimEnd('0').Length > 2) throw new BezahlCodeException("Amount must have less than 3 digits after decimal point."); if (amount < 0.01m || amount > 999999999.99m) throw new BezahlCodeException("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); @@ -221,11 +221,11 @@ public BezahlCode(AuthorityType authority, string name, string account, string b throw new BezahlCodeException("Execution date must be today or in future."); _executionDate = (DateTime)executionDate; } -#pragma warning disable CS0612 +#pragma warning disable CS0618 if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.periodicsinglepaymentsepa) -#pragma warning restore CS0612 +#pragma warning restore CS0618 { - if (periodicTimeunit.ToUpper() != "M" && periodicTimeunit.ToUpper() != "W") + if (periodicTimeunit.ToUpperInvariant() != "M" && periodicTimeunit.ToUpperInvariant() != "W") throw new BezahlCodeException("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); _periodicTimeunit = periodicTimeunit; if (periodicTimeunitRotation < 1 || periodicTimeunitRotation > 52) @@ -253,9 +253,9 @@ public override string ToString() if (_authority != AuthorityType.contact && _authority != AuthorityType.contact_v2) { //Handle what is same for all payments -#pragma warning disable CS0612 +#pragma warning disable CS0618 if (_authority == AuthorityType.periodicsinglepayment || _authority == AuthorityType.singledirectdebit || _authority == AuthorityType.singlepayment) -#pragma warning restore CS0612 +#pragma warning restore CS0618 { bezahlCodePayload += $"account={_account}&"; bezahlCodePayload += $"bnc={_bnc}&"; @@ -277,26 +277,26 @@ public override string ToString() if (!string.IsNullOrEmpty(_mandateId)) bezahlCodePayload += $"mandateid={Uri.EscapeDataString(_mandateId)}&"; if (_dateOfSignature != DateTime.MinValue) - bezahlCodePayload += $"dateofsignature={_dateOfSignature.ToString("ddMMyyyy")}&"; + bezahlCodePayload += $"dateofsignature={_dateOfSignature.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&"; } } - bezahlCodePayload += $"amount={_amount:0.00}&".Replace(".", ","); + bezahlCodePayload += string.Format(CultureInfo.InvariantCulture, "amount={0:0.00}&", _amount).Replace(".", ","); if (!string.IsNullOrEmpty(_reason)) bezahlCodePayload += $"reason={Uri.EscapeDataString(_reason)}&"; bezahlCodePayload += $"currency={_currency}&"; - bezahlCodePayload += $"executiondate={_executionDate.ToString("ddMMyyyy")}&"; -#pragma warning disable CS0612 + bezahlCodePayload += $"executiondate={_executionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&"; +#pragma warning disable CS0618 if (_authority == AuthorityType.periodicsinglepayment || _authority == AuthorityType.periodicsinglepaymentsepa) { bezahlCodePayload += $"periodictimeunit={_periodicTimeunit}&"; bezahlCodePayload += $"periodictimeunitrotation={_periodicTimeunitRotation}&"; if (_periodicFirstExecutionDate != DateTime.MinValue) - bezahlCodePayload += $"periodicfirstexecutiondate={_periodicFirstExecutionDate.ToString("ddMMyyyy")}&"; + bezahlCodePayload += $"periodicfirstexecutiondate={_periodicFirstExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&"; if (_periodicLastExecutionDate != DateTime.MinValue) - bezahlCodePayload += $"periodiclastexecutiondate={_periodicLastExecutionDate.ToString("ddMMyyyy")}&"; + bezahlCodePayload += $"periodiclastexecutiondate={_periodicLastExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&"; } -#pragma warning restore CS0612 +#pragma warning restore CS0618 } else { @@ -1047,6 +1047,7 @@ public enum Currency } +#pragma warning disable CA1707 // Underscore in identifier /// /// Operation modes of the BezahlCode /// @@ -1055,7 +1056,7 @@ public enum AuthorityType /// /// Single payment (Überweisung) /// - [Obsolete] + [Obsolete("Use singlepaymentsepa instead for SEPA-compliant payments")] singlepayment, /// /// Single SEPA payment (SEPA-Überweisung) @@ -1064,7 +1065,7 @@ public enum AuthorityType /// /// Single debit (Lastschrift) /// - [Obsolete] + [Obsolete("Use singledirectdebitsepa instead for SEPA-compliant payments")] singledirectdebit, /// /// Single SEPA debit (SEPA-Lastschrift) @@ -1073,7 +1074,7 @@ public enum AuthorityType /// /// Periodic payment (Dauerauftrag) /// - [Obsolete] + [Obsolete("Use periodicsinglepaymentsepa instead for SEPA-compliant payments")] periodicsinglepayment, /// /// Periodic SEPA payment (SEPA-Dauerauftrag) @@ -1088,6 +1089,7 @@ public enum AuthorityType /// contact_v2 } +#pragma warning restore CA1707 // Underscore in identifier /// /// Exception class for BezahlCode errors. diff --git a/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs b/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs index f2c5cf3f..d9f05245 100644 --- a/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs +++ b/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs @@ -61,7 +61,7 @@ public override string ToString() .ToArray()); } - return $"{Enum.GetName(typeof(BitcoinLikeCryptoCurrencyType), _currencyType)!.ToLower()}:{_address}{query}"; + return $"{Enum.GetName(typeof(BitcoinLikeCryptoCurrencyType), _currencyType)!.ToLowerInvariant()}:{_address}{query}"; } /// diff --git a/QRCoder/PayloadGenerator/CalendarEvent.cs b/QRCoder/PayloadGenerator/CalendarEvent.cs index 8a33dfc5..d59321d6 100644 --- a/QRCoder/PayloadGenerator/CalendarEvent.cs +++ b/QRCoder/PayloadGenerator/CalendarEvent.cs @@ -50,8 +50,8 @@ public CalendarEvent(string subject, string? description, string? location, Date if (end.Kind == DateTimeKind.Utc) dtFormatEnd = "yyyyMMddTHHmmssZ"; } - _start = start.ToString(dtFormatStart); - _end = end.ToString(dtFormatEnd); + _start = start.ToString(dtFormatStart, CultureInfo.InvariantCulture); + _end = end.ToString(dtFormatEnd, CultureInfo.InvariantCulture); } /// diff --git a/QRCoder/PayloadGenerator/ContactData.cs b/QRCoder/PayloadGenerator/ContactData.cs index f31d7e0d..09fefd53 100644 --- a/QRCoder/PayloadGenerator/ContactData.cs +++ b/QRCoder/PayloadGenerator/ContactData.cs @@ -136,7 +136,7 @@ public override string ToString() if (!string.IsNullOrEmpty(_note)) payload += $"NOTE:{_note}\r\n"; if (_birthday != null) - payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd")}\r\n"; + payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd", CultureInfo.InvariantCulture)}\r\n"; // RFC 2426 Section 3.2.1: ADR format is PO Box; Extended Address; Street; Locality (City); Region; Postal Code; Country string addressString = string.Empty; if (_addressOrder == AddressOrder.Default) @@ -232,7 +232,7 @@ public override string ToString() payload += addressString; if (_birthday != null) - payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd")}\r\n"; + payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd", CultureInfo.InvariantCulture)}\r\n"; if (!string.IsNullOrEmpty(_website)) payload += $"URL:{_website}\r\n"; if (!string.IsNullOrEmpty(_email)) diff --git a/QRCoder/PayloadGenerator/Girocode.cs b/QRCoder/PayloadGenerator/Girocode.cs index 54c06671..fedd794d 100644 --- a/QRCoder/PayloadGenerator/Girocode.cs +++ b/QRCoder/PayloadGenerator/Girocode.cs @@ -42,14 +42,14 @@ public Girocode(string iban, string? bic, string name, decimal amount, string re _encoding = encoding; if (!IsValidIban(iban)) throw new GirocodeException("The IBAN entered isn't valid."); - _iban = iban.Replace(" ", "").ToUpper(); + _iban = iban.Replace(" ", "").ToUpperInvariant(); if (!IsValidBic(bic, _version == GirocodeVersion.Version1)) throw new GirocodeException("The BIC entered isn't valid."); - _bic = bic?.Replace(" ", "").ToUpper() ?? string.Empty; + _bic = bic?.Replace(" ", "").ToUpperInvariant() ?? string.Empty; if (name.Length > 70) throw new GirocodeException("(Payee-)Name must be shorter than 71 chars."); _name = name; - if (amount.ToString().Replace(",", ".").Contains(".") && amount.ToString().Replace(",", ".").Split('.')[1].TrimEnd('0').Length > 2) + if (amount.ToString(CultureInfo.InvariantCulture).Contains('.') && amount.ToString(CultureInfo.InvariantCulture).Split('.')[1].TrimEnd('0').Length > 2) throw new GirocodeException("Amount must have less than 3 digits after decimal point."); if (amount < 0.01m || amount > 999999999.99m) throw new GirocodeException("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); @@ -81,7 +81,7 @@ public override string ToString() girocodePayload += _bic + _br; girocodePayload += _name + _br; girocodePayload += _iban + _br; - girocodePayload += $"EUR{_amount:0.00}".Replace(",", ".") + _br; + girocodePayload += string.Format(CultureInfo.InvariantCulture, "EUR{0:0.00}", _amount) + _br; girocodePayload += _purposeOfCreditTransfer + _br; girocodePayload += ((_typeOfRemittance == TypeOfRemittance.Structured) ? _remittanceInformation @@ -126,6 +126,7 @@ public enum TypeOfRemittance Unstructured } +#pragma warning disable CA1707 // Underscore in identifier /// /// Defines the encoding types for Girocode payloads. /// @@ -171,6 +172,7 @@ public enum GirocodeEncoding /// ISO_8859_15 } +#pragma warning restore CA1707 // Underscore in identifier /// /// Represents errors that occur during Girocode generation. diff --git a/QRCoder/PayloadGenerator/MoneroTransaction.cs b/QRCoder/PayloadGenerator/MoneroTransaction.cs index 7628d08c..1ff7292b 100644 --- a/QRCoder/PayloadGenerator/MoneroTransaction.cs +++ b/QRCoder/PayloadGenerator/MoneroTransaction.cs @@ -42,7 +42,7 @@ public override string ToString() var moneroUri = $"monero://{_address}{(!string.IsNullOrEmpty(_txPaymentId) || !string.IsNullOrEmpty(_recipientName) || !string.IsNullOrEmpty(_txDescription) || _txAmount != null ? "?" : string.Empty)}"; moneroUri += (!string.IsNullOrEmpty(_txPaymentId) ? $"tx_payment_id={Uri.EscapeDataString(_txPaymentId)}&" : string.Empty); moneroUri += (!string.IsNullOrEmpty(_recipientName) ? $"recipient_name={Uri.EscapeDataString(_recipientName)}&" : string.Empty); - moneroUri += (_txAmount != null ? $"tx_amount={_txAmount.ToString()!.Replace(",", ".")}&" : string.Empty); + moneroUri += (_txAmount != null ? $"tx_amount={_txAmount.Value.ToString(CultureInfo.InvariantCulture)}&" : string.Empty); moneroUri += (!string.IsNullOrEmpty(_txDescription) ? $"tx_description={Uri.EscapeDataString(_txDescription)}" : string.Empty); return moneroUri.TrimEnd('&'); } diff --git a/QRCoder/PayloadGenerator/OneTimePassword.cs b/QRCoder/PayloadGenerator/OneTimePassword.cs index 36bf6a02..e81eeb87 100644 --- a/QRCoder/PayloadGenerator/OneTimePassword.cs +++ b/QRCoder/PayloadGenerator/OneTimePassword.cs @@ -52,7 +52,7 @@ public OoneTimePasswordAuthAlgorithm Algorithm /// /// The counter value for HOTP (only used if Type is HOTP). /// - public int? Counter { get; set; } = null; + public int? Counter { get; set; } /// /// The period in seconds for TOTP (default is 30). @@ -153,7 +153,7 @@ private string TimeToString() { if (Period == null) { - throw new Exception("Period must be set when using OneTimePasswordAuthType.TOTP"); + throw new InvalidOperationException("Period must be set when using OneTimePasswordAuthType.TOTP"); } var sb = new StringBuilder("otpauth://totp/"); @@ -176,7 +176,7 @@ private void ProcessCommonFields(StringBuilder sb) { if (Secret.IsNullOrWhiteSpace()) { - throw new Exception("Secret must be a filled out base32 encoded string"); + throw new InvalidOperationException("Secret must be a filled out base32 encoded string"); } string strippedSecret = Secret.Replace(" ", ""); string? escapedIssuer = null; @@ -185,18 +185,18 @@ private void ProcessCommonFields(StringBuilder sb) if (!Issuer.IsNullOrWhiteSpace()) { - if (Issuer.Contains(":")) + if (Issuer.Contains(':')) { - throw new Exception("Issuer must not have a ':'"); + throw new InvalidOperationException("Issuer must not have a ':'"); } escapedIssuer = Uri.EscapeDataString(Issuer); } if (!Label.IsNullOrWhiteSpace()) { - if (Label.Contains(":")) + if (Label.Contains(':')) { - throw new Exception("Label must not have a ':'"); + throw new InvalidOperationException("Label must not have a ':'"); } escapedLabel = Uri.EscapeDataString(Label); } diff --git a/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs b/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs index f31f4410..1ecd7194 100644 --- a/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs +++ b/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs @@ -90,7 +90,7 @@ public byte[] ToBytes() _separator = DetermineSeparator(); //Create the payload string - string ret = $"ST0001" + ((int)_characterSet).ToString() + //(separator != "|" ? separator : "") + + string ret = $"ST0001" + ((int)_characterSet).ToString(CultureInfo.InvariantCulture) + //(separator != "|" ? separator : "") + $"{_separator}Name={_mFields.Name}" + $"{_separator}PersonalAcc={_mFields.PersonalAcc}" + $"{_separator}BankName={_mFields.BankName}" + @@ -156,7 +156,7 @@ private List GetOptionalFieldsAsList() .Select(field => { var objValue = field.GetValue(_oFields, null); - var value = field.PropertyType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy") : objValue!.ToString(); + var value = field.PropertyType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) : objValue!.ToString(); return $"{field.Name}={value}"; }) .ToList(); @@ -186,7 +186,7 @@ private List GetMandatoryFieldsAsList() .Select(field => { var objValue = field.GetValue(_mFields); - var value = field.FieldType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy") : objValue!.ToString(); + var value = field.FieldType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) : objValue!.ToString(); return $"{field.Name}={value}"; }) .ToList(); @@ -575,6 +575,7 @@ public string? TaxPaytKind public TechCode? TechCode { get; set; } } +#pragma warning disable CA1707 // Underscore in identifier /// /// (List of values of the technical code of the payment) /// Перечень значений технического кода платежа @@ -692,6 +693,7 @@ public enum CharacterSets /// koi8_r = 3 } +#pragma warning restore CA1707 // Underscore in identifier /// /// Represents errors that occur during the generation of a RussiaPaymentOrder payload. diff --git a/QRCoder/PayloadGenerator/SMS.cs b/QRCoder/PayloadGenerator/SMS.cs index 6d167c22..935ab35f 100644 --- a/QRCoder/PayloadGenerator/SMS.cs +++ b/QRCoder/PayloadGenerator/SMS.cs @@ -47,6 +47,7 @@ public SMS(string number, string subject, SMSEncoding encoding = SMSEncoding.SMS _ => string.Empty, }; +#pragma warning disable CA1707 // Underscore in identifier /// /// Specifies the encoding type for the SMS payload. /// @@ -65,5 +66,6 @@ public enum SMSEncoding /// SMS_iOS } +#pragma warning restore CA1707 // Underscore in identifier } } diff --git a/QRCoder/PayloadGenerator/SlovenianUpnQr.cs b/QRCoder/PayloadGenerator/SlovenianUpnQr.cs index cdf4776b..8a7f9fb5 100644 --- a/QRCoder/PayloadGenerator/SlovenianUpnQr.cs +++ b/QRCoder/PayloadGenerator/SlovenianUpnQr.cs @@ -45,7 +45,7 @@ public class SlovenianUpnQr : Payload /// The string to limit. /// The maximum length of the string. /// The limited string. - private string LimitLength(string value, int maxLength) + private static string LimitLength(string value, int maxLength) => (value.Length <= maxLength) ? value : value.Substring(0, maxLength); /// @@ -89,14 +89,14 @@ public SlovenianUpnQr(string payerName, string payerAddress, string payerPlace, _payerAddress = LimitLength(payerAddress.Trim(), 33); _payerPlace = LimitLength(payerPlace.Trim(), 33); _amount = FormatAmount(amount); - _code = LimitLength(code.Trim().ToUpper(), 4); + _code = LimitLength(code.Trim().ToUpperInvariant(), 4); _purpose = LimitLength(description.Trim(), 42); - _deadLine = (deadline == null) ? "" : deadline.Value.ToString("dd.MM.yyyy"); + _deadLine = (deadline == null) ? "" : deadline.Value.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture); _recipientIban = LimitLength(recipientIban.Trim(), 34); _recipientName = LimitLength(recipientName.Trim(), 33); _recipientAddress = LimitLength(recipientAddress.Trim(), 33); _recipientPlace = LimitLength(recipientPlace.Trim(), 33); - _recipientSiModel = LimitLength(recipientSiModel.Trim().ToUpper(), 4); + _recipientSiModel = LimitLength(recipientSiModel.Trim().ToUpperInvariant(), 4); _recipientSiReference = LimitLength(recipientSiReference.Trim(), 22); } @@ -106,10 +106,10 @@ public SlovenianUpnQr(string payerName, string payerAddress, string payerPlace, /// /// The amount to format. /// The formatted amount string. - private string FormatAmount(double amount) + private static string FormatAmount(double amount) { int _amt = (int)Math.Round(amount * 100.0); - return string.Format("{0:00000000000}", _amt); + return string.Format(CultureInfo.InvariantCulture, "{0:00000000000}", _amt); } /// @@ -148,15 +148,15 @@ public override string ToString() _sb.Append(_payerAddress).Append('\n'); _sb.Append(_payerPlace).Append('\n'); _sb.Append(_amount).Append('\n').Append('\n').Append('\n'); - _sb.Append(_code.ToUpper()).Append('\n'); + _sb.Append(_code.ToUpperInvariant()).Append('\n'); _sb.Append(_purpose).Append('\n'); _sb.Append(_deadLine).Append('\n'); - _sb.Append(_recipientIban.ToUpper()).Append('\n'); + _sb.Append(_recipientIban.ToUpperInvariant()).Append('\n'); _sb.Append(_recipientSiModel).Append(_recipientSiReference).Append('\n'); _sb.Append(_recipientName).Append('\n'); _sb.Append(_recipientAddress).Append('\n'); _sb.Append(_recipientPlace).Append('\n'); - _sb.AppendFormat("{0:000}", CalculateChecksum()).Append('\n'); + _sb.AppendFormat(CultureInfo.InvariantCulture, "{0:000}", CalculateChecksum()).Append('\n'); return _sb.ToString(); } } diff --git a/QRCoder/PayloadGenerator/SwissQrCode.cs b/QRCoder/PayloadGenerator/SwissQrCode.cs index ea1ed9fe..57384154 100644 --- a/QRCoder/PayloadGenerator/SwissQrCode.cs +++ b/QRCoder/PayloadGenerator/SwissQrCode.cs @@ -52,7 +52,7 @@ public SwissQrCode(Iban iban, Currency currency, Contact creditor, Reference ref _additionalInformation = additionalInformation ?? new AdditionalInformation(); - if (amount != null && amount.ToString()!.Length > 12) + if (amount != null && amount.Value.ToString(CultureInfo.InvariantCulture).Length > 12) throw new SwissQrCodeException("Amount (including decimals) must be shorter than 13 places."); _amount = amount; @@ -275,7 +275,7 @@ public Iban(string iban, IbanType ibanType) throw new SwissQrCodeIbanException("The IBAN entered isn't valid."); if (ibanType == IbanType.QrIban && !IsValidQRIban(iban)) throw new SwissQrCodeIbanException("The QR-IBAN entered isn't valid."); - if (!iban.StartsWith("CH") && !iban.StartsWith("LI")) + if (!iban.StartsWith("CH", StringComparison.Ordinal) && !iban.StartsWith("LI", StringComparison.Ordinal)) throw new SwissQrCodeIbanException("The IBAN must start with \"CH\" or \"LI\"."); _iban = iban; _ibanType = ibanType; @@ -591,7 +591,7 @@ public override string ToString() SwissQrCodePayload += _alternativeProcedure2!.Replace("\n", "") + _br; //AltPmt //S-QR specification 2.0, chapter 4.2.3 - if (SwissQrCodePayload.EndsWith(_br)) + if (SwissQrCodePayload.EndsWith(_br, StringComparison.Ordinal)) SwissQrCodePayload = SwissQrCodePayload.Remove(SwissQrCodePayload.Length - _br.Length); return SwissQrCodePayload; diff --git a/QRCoder/PdfByteQRCode.cs b/QRCoder/PdfByteQRCode.cs index 5b90a707..d534051e 100644 --- a/QRCoder/PdfByteQRCode.cs +++ b/QRCoder/PdfByteQRCode.cs @@ -31,21 +31,6 @@ public PdfByteQRCode(QRCodeData data) : base(data) { } public byte[] GetGraphic(int pixelsPerModule) => GetGraphic(pixelsPerModule, "#000000", "#ffffff"); - /// - /// Converts a hexadecimal color string to a byte array. - /// - /// Color in HEX format like #ffffff. - /// Returns the color as a byte array. - private byte[] HexColorToByteArray(string colorString) - { - if (colorString.StartsWith("#")) - colorString = colorString.Substring(1); - byte[] byteColor = new byte[colorString.Length / 2]; - for (int i = 0; i < byteColor.Length; i++) - byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); - return byteColor; - } - /// /// Creates a PDF document with specified colors and DPI. /// @@ -63,8 +48,8 @@ public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string li var pdfMediaSize = ToStr(imgSize * 72 / (float)dpi); // Parse colors - var darkColor = HexColorToByteArray(darkColorHtmlHex); - var lightColor = HexColorToByteArray(lightColorHtmlHex); + var darkColor = darkColorHtmlHex.HexColorToByteArray(); + var lightColor = lightColorHtmlHex.HexColorToByteArray(); var darkColorPdf = ColorToPdfRgb(darkColor); var lightColorPdf = ColorToPdfRgb(lightColor); diff --git a/QRCoder/PostscriptQRCode.cs b/QRCoder/PostscriptQRCode.cs index e57a4204..c3064644 100644 --- a/QRCoder/PostscriptQRCode.cs +++ b/QRCoder/PostscriptQRCode.cs @@ -100,11 +100,11 @@ public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool d var drawableModulesCount = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2); var pointsPerModule = (double)Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount; - string psFile = string.Format(PS_HEADER, new object[] { + string psFile = string.Format(CultureInfo.InvariantCulture, PS_HEADER, new object[] { CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule), epsFormat ? "EPSF-3.0" : string.Empty }); - psFile += string.Format(PS_FUNCTIONS, new object[] { + psFile += string.Format(CultureInfo.InvariantCulture, PS_FUNCTIONS, new object[] { CleanSvgVal(darkColor.R /255.0), CleanSvgVal(darkColor.G /255.0), CleanSvgVal(darkColor.B /255.0), CleanSvgVal(lightColor.R /255.0), CleanSvgVal(lightColor.G /255.0), CleanSvgVal(lightColor.B /255.0), drawableModulesCount @@ -128,7 +128,7 @@ public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool d /// /// The input double value. /// Returns the cleaned string representation of the double value. - private string CleanSvgVal(double input) => input.ToString(System.Globalization.CultureInfo.InvariantCulture); + private static string CleanSvgVal(double input) => input.ToString(System.Globalization.CultureInfo.InvariantCulture); // Note: line terminations here will encode differently based on which platform QRCoder was compiled on (CRLF vs LF); // however, PostScript interpreters should handle both equally well. diff --git a/QRCoder/QRCode.cs b/QRCoder/QRCode.cs index 44705511..ceb02fdd 100644 --- a/QRCoder/QRCode.cs +++ b/QRCoder/QRCode.cs @@ -152,7 +152,7 @@ public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, /// The rectangle for which to create the rounded path. /// The radius of the corners. /// Returns the rounded rectangle path. - internal GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius) + internal static GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius) { var roundedRect = new GraphicsPath(); roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90); diff --git a/QRCoder/QRCodeData.cs b/QRCoder/QRCodeData.cs index d1d3611f..b8d56643 100644 --- a/QRCoder/QRCodeData.cs +++ b/QRCoder/QRCodeData.cs @@ -78,23 +78,23 @@ public QRCodeData(byte[] rawData, Compression compressMode) } if (rawData.Length < 5) - throw new Exception("Invalid raw data file. File too short."); + throw new InvalidDataException("Invalid raw data file. File too short."); if (rawData[0] != 0x51 || rawData[1] != 0x52 || rawData[2] != 0x52) - throw new Exception("Invalid raw data file. Filetype doesn't match \"QRR\"."); + throw new InvalidDataException("Invalid raw data file. Filetype doesn't match \"QRR\"."); // Set QR code version from side length (includes 8-module quiet zone) var sideLen = (int)rawData[4]; if (sideLen < 29) // Micro QR: sideLen = 19 + 2*(m-1), m in [1..4] -> versions -1..-4 { if (((sideLen - 19) & 1) != 0) - throw new Exception("Invalid raw data file. Side length not valid for Micro QR."); + throw new InvalidDataException("Invalid raw data file. Side length not valid for Micro QR."); var m = ((sideLen - 19) / 2) + 1; Version = -m; } else // Standard QR: sideLen = 29 + 4*(v-1), v in [1..40] { if (((sideLen - 29) % 4) != 0) - throw new Exception("Invalid raw data file. Side length not valid for QR."); + throw new InvalidDataException("Invalid raw data file. Side length not valid for QR."); Version = ((sideLen - 29) / 4) + 1; } @@ -219,11 +219,11 @@ private static int ModulesPerSideFromVersion(int version) /// /// Releases all resources used by the . /// - public void Dispose() + public virtual void Dispose() { ModuleMatrix = null!; Version = 0; - + GC.SuppressFinalize(this); } /// diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 020d7302..4970f1aa 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -24,7 +24,9 @@ public QRCodeGenerator() /// A payload object, generated by the PayloadGenerator-class /// Thrown when the payload is too big to be encoded in a QR code. /// Returns the raw QR code data which can be used for rendering. +#pragma warning disable CA1822 // Mark members as static public QRCodeData CreateQrCode(PayloadGenerator.Payload payload) +#pragma warning restore CA1822 // Mark members as static => GenerateQrCode(payload); /// @@ -34,7 +36,9 @@ public QRCodeData CreateQrCode(PayloadGenerator.Payload payload) /// The level of error correction data /// Thrown when the payload is too big to be encoded in a QR code. /// Returns the raw QR code data which can be used for rendering. +#pragma warning disable CA1822 // Mark members as static public QRCodeData CreateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) +#pragma warning restore CA1822 // Mark members as static => GenerateQrCode(payload, eccLevel); /// @@ -48,7 +52,9 @@ public QRCodeData CreateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLev /// Set fixed QR code target version. /// Thrown when the payload is too big to be encoded in a QR code. /// Returns the raw QR code data which can be used for rendering. +#pragma warning disable CA1822 // Mark members as static public QRCodeData CreateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1) +#pragma warning restore CA1822 // Mark members as static => GenerateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); /// @@ -58,7 +64,9 @@ public QRCodeData CreateQrCode(string plainText, ECCLevel eccLevel, bool forceUt /// The level of error correction data /// Thrown when the payload is too big to be encoded in a QR code. /// Returns the raw QR code data which can be used for rendering. +#pragma warning disable CA1822 // Mark members as static public QRCodeData CreateQrCode(byte[] binaryData, ECCLevel eccLevel) +#pragma warning restore CA1822 // Mark members as static => GenerateQrCode(binaryData, eccLevel); @@ -192,7 +200,7 @@ public static QRCodeData GenerateMicroQrCode(string plainText, ECCLevel eccLevel .First(x => (x.ErrorCorrectionLevel == eccLevel || (eccLevel == ECCLevel.Default && x.ErrorCorrectionLevel == ECCLevel.L))) .CapacityDict.TryGetValue(encoding, out var maxSizeByte); if (!matchedEncoding) - throw new ArgumentOutOfRangeException(nameof(encoding), encoding, "Encoding not supported for this version."); + throw new InvalidOperationException("Required encoding is not supported for this version."); throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encoding.ToString(), version, maxSizeByte); } } @@ -1368,8 +1376,9 @@ static int[] GetNotUniqueExponents(Polynom list) } /// - public void Dispose() + public virtual void Dispose() { // left for back-compat + GC.SuppressFinalize(this); } } diff --git a/QRCoder/QRCodeGenerator/EciMode.cs b/QRCoder/QRCodeGenerator/EciMode.cs index 4cf253be..d73fd27a 100644 --- a/QRCoder/QRCodeGenerator/EciMode.cs +++ b/QRCoder/QRCodeGenerator/EciMode.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1707 // Underscore in identifier + namespace QRCoder; public partial class QRCodeGenerator diff --git a/QRCoder/QRCodeGenerator/Point.cs b/QRCoder/QRCodeGenerator/Point.cs index 36255b46..6ec9d532 100644 --- a/QRCoder/QRCodeGenerator/Point.cs +++ b/QRCoder/QRCodeGenerator/Point.cs @@ -41,5 +41,16 @@ public Point(int x, int y) /// public bool Equals(Point other) => X == other.X && Y == other.Y; + + /// + public override bool Equals(object? obj) => obj is Point point && Equals(point); + + /// + public override int GetHashCode() +#if NET5_0_OR_GREATER + => HashCode.Combine(X, Y); +#else + => X ^ (int)(((uint)Y << 16) | ((uint)Y >> 16)); +#endif } } diff --git a/QRCoder/QRCodeGenerator/Polynom.cs b/QRCoder/QRCodeGenerator/Polynom.cs index 1e6e9a57..90237945 100644 --- a/QRCoder/QRCodeGenerator/Polynom.cs +++ b/QRCoder/QRCodeGenerator/Polynom.cs @@ -36,7 +36,7 @@ public void Add(PolynomItem item) public void RemoveAt(int index) { if ((uint)index >= (uint)Count) - throw new IndexOutOfRangeException(); + ThrowIndexArgumentOutOfRangeException(); if (index < Count - 1) Array.Copy(_polyItems, index + 1, _polyItems, index, Count - index - 1); @@ -52,13 +52,13 @@ public PolynomItem this[int index] get { if ((uint)index >= Count) - ThrowIndexOutOfRangeException(); + ThrowIndexArgumentOutOfRangeException(); return _polyItems[index]; } set { if ((uint)index >= Count) - ThrowIndexOutOfRangeException(); + ThrowIndexArgumentOutOfRangeException(); _polyItems[index] = value; } } @@ -66,7 +66,7 @@ public PolynomItem this[int index] #if NET6_0_OR_GREATER [StackTraceHidden] #endif - private static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException(); + private static void ThrowIndexArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException("index"); /// diff --git a/QRCoder/SvgQRCode.cs b/QRCoder/SvgQRCode.cs index 7b34be0f..1079baae 100644 --- a/QRCoder/SvgQRCode.cs +++ b/QRCoder/SvgQRCode.cs @@ -152,7 +152,9 @@ public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex } var svgFile = new StringBuilder($@""); +#pragma warning disable CA1305 // Specify IFormatProvider svgFile.AppendLine($@""); +#pragma warning restore CA1305 // Specify IFormatProvider for (int yi = 0; yi < drawableModulesCount; yi += 1) { double y = yi * pixelsPerModule; @@ -179,7 +181,9 @@ public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex // Output SVG rectangles double x = xi * pixelsPerModule; if (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(x, y, logoAttr!.Value, pixelsPerModule)) +#pragma warning disable CA1305 // Specify IFormatProvider svgFile.AppendLine($@""); +#pragma warning restore CA1305 // Specify IFormatProvider } } } @@ -190,7 +194,9 @@ public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex if (!logo.IsEmbedded()) { svgFile.AppendLine($@""); +#pragma warning disable CA1305 // Specify IFormatProvider svgFile.AppendLine($@""); +#pragma warning restore CA1305 // Specify IFormatProvider svgFile.AppendLine(@""); } else @@ -210,10 +216,10 @@ public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex return svgFile.ToString(); } - private bool IsBlockedByLogo(double x, double y, ImageAttributes attr, double pixelPerModule) + private static bool IsBlockedByLogo(double x, double y, ImageAttributes attr, double pixelPerModule) => x + pixelPerModule >= attr.X && x <= attr.X + attr.Width && y + pixelPerModule >= attr.Y && y <= attr.Y + attr.Height; - private ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox) + private static ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox) { var imgWidth = logo.GetIconSizePercent() / 100d * viewBox.Width; var imgHeight = logo.GetIconSizePercent() / 100d * viewBox.Height; @@ -239,7 +245,7 @@ private struct ImageAttributes //Clean double values for international use/formats //We use explicitly "G15" to avoid differences between .NET full and Core platforms //https://stackoverflow.com/questions/64898117/tostring-has-a-different-behavior-between-net-462-and-net-core-3-1 - private string CleanSvgVal(double input) + private static string CleanSvgVal(double input) => input.ToString("G15", System.Globalization.CultureInfo.InvariantCulture); /// @@ -384,7 +390,7 @@ public enum MediaType : int SVG = 1 } - private string GetMimeType(MediaType type) => type switch + private static string GetMimeType(MediaType type) => type switch { MediaType.PNG => "image/png", MediaType.SVG => "image/svg+xml", diff --git a/QRCoderApiTests/ApiApprovalTests.cs b/QRCoderApiTests/ApiApprovalTests.cs index e9dff5b9..77f3a5e9 100644 --- a/QRCoderApiTests/ApiApprovalTests.cs +++ b/QRCoderApiTests/ApiApprovalTests.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1861 // Prefer 'static readonly' fields over constant arrays + using System.Diagnostics; using System.Reflection; using System.Xml.Linq; @@ -61,7 +63,7 @@ public void PublicApi(Type type) string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries); // There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out - string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray(); + string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm, StringComparison.Ordinal))).ToArray(); Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}"); (string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions diff --git a/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt b/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt index a700b557..03d31fc1 100644 --- a/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt +++ b/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt @@ -143,13 +143,13 @@ namespace QRCoder public override string ToString() { } public enum AuthorityType { - [System.Obsolete] + [System.Obsolete("Use singlepaymentsepa instead for SEPA-compliant payments")] singlepayment = 0, singlepaymentsepa = 1, - [System.Obsolete] + [System.Obsolete("Use singledirectdebitsepa instead for SEPA-compliant payments")] singledirectdebit = 2, singledirectdebitsepa = 3, - [System.Obsolete] + [System.Obsolete("Use periodicsinglepaymentsepa instead for SEPA-compliant payments")] periodicsinglepayment = 4, periodicsinglepaymentsepa = 5, contact = 6, @@ -905,7 +905,7 @@ namespace QRCoder public QRCodeData(string pathToRawData, QRCoder.QRCodeData.Compression compressMode) { } public System.Collections.Generic.List ModuleMatrix { get; set; } public int Version { get; } - public void Dispose() { } + public virtual void Dispose() { } public byte[] GetRawData(QRCoder.QRCodeData.Compression compressMode) { } public void SaveRawData(string filePath, QRCoder.QRCodeData.Compression compressMode) { } public enum Compression @@ -922,7 +922,7 @@ namespace QRCoder public QRCoder.QRCodeData CreateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { } - public void Dispose() { } + public virtual void Dispose() { } public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { } public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { } public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } @@ -1003,6 +1003,7 @@ namespace QRCoder.Extensions { public static string? GetStringValue(this System.Enum value) { } } + [System.AttributeUsage(System.AttributeTargets.Field)] [System.Obsolete("This attribute will be removed in a future version of QRCoder.")] public class StringValueAttribute : System.Attribute { diff --git a/QRCoderApiTests/net60/QRCoder.approved.txt b/QRCoderApiTests/net60/QRCoder.approved.txt index d50a8826..37fde645 100644 --- a/QRCoderApiTests/net60/QRCoder.approved.txt +++ b/QRCoderApiTests/net60/QRCoder.approved.txt @@ -148,13 +148,13 @@ namespace QRCoder public override string ToString() { } public enum AuthorityType { - [System.Obsolete] + [System.Obsolete("Use singlepaymentsepa instead for SEPA-compliant payments")] singlepayment = 0, singlepaymentsepa = 1, - [System.Obsolete] + [System.Obsolete("Use singledirectdebitsepa instead for SEPA-compliant payments")] singledirectdebit = 2, singledirectdebitsepa = 3, - [System.Obsolete] + [System.Obsolete("Use periodicsinglepaymentsepa instead for SEPA-compliant payments")] periodicsinglepayment = 4, periodicsinglepaymentsepa = 5, contact = 6, @@ -911,7 +911,7 @@ namespace QRCoder public QRCodeData(string pathToRawData, QRCoder.QRCodeData.Compression compressMode) { } public System.Collections.Generic.List ModuleMatrix { get; set; } public int Version { get; } - public void Dispose() { } + public virtual void Dispose() { } public byte[] GetRawData(QRCoder.QRCodeData.Compression compressMode) { } public void SaveRawData(string filePath, QRCoder.QRCodeData.Compression compressMode) { } public enum Compression @@ -928,7 +928,7 @@ namespace QRCoder public QRCoder.QRCodeData CreateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { } - public void Dispose() { } + public virtual void Dispose() { } public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { } public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { } public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } @@ -1012,6 +1012,7 @@ namespace QRCoder.Extensions [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("This method uses reflection to examine the provided enum value.")] public static string? GetStringValue(this System.Enum value) { } } + [System.AttributeUsage(System.AttributeTargets.Field)] [System.Obsolete("This attribute will be removed in a future version of QRCoder.")] public class StringValueAttribute : System.Attribute { diff --git a/QRCoderApiTests/netstandard13/QRCoder.approved.txt b/QRCoderApiTests/netstandard13/QRCoder.approved.txt index d8a1d7e6..f127e9ef 100644 --- a/QRCoderApiTests/netstandard13/QRCoder.approved.txt +++ b/QRCoderApiTests/netstandard13/QRCoder.approved.txt @@ -86,13 +86,13 @@ namespace QRCoder public override string ToString() { } public enum AuthorityType { - [System.Obsolete] + [System.Obsolete("Use singlepaymentsepa instead for SEPA-compliant payments")] singlepayment = 0, singlepaymentsepa = 1, - [System.Obsolete] + [System.Obsolete("Use singledirectdebitsepa instead for SEPA-compliant payments")] singledirectdebit = 2, singledirectdebitsepa = 3, - [System.Obsolete] + [System.Obsolete("Use periodicsinglepaymentsepa instead for SEPA-compliant payments")] periodicsinglepayment = 4, periodicsinglepaymentsepa = 5, contact = 6, @@ -823,7 +823,7 @@ namespace QRCoder public QRCodeData(string pathToRawData, QRCoder.QRCodeData.Compression compressMode) { } public System.Collections.Generic.List ModuleMatrix { get; set; } public int Version { get; } - public void Dispose() { } + public virtual void Dispose() { } public byte[] GetRawData(QRCoder.QRCodeData.Compression compressMode) { } public void SaveRawData(string filePath, QRCoder.QRCodeData.Compression compressMode) { } public enum Compression @@ -840,7 +840,7 @@ namespace QRCoder public QRCoder.QRCodeData CreateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } public QRCoder.QRCodeData CreateQrCode(byte[] binaryData, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } public QRCoder.QRCodeData CreateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { } - public void Dispose() { } + public virtual void Dispose() { } public static QRCoder.QRCodeData GenerateMicroQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel = -1, int requestedVersion = 0) { } public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload) { } public static QRCoder.QRCodeData GenerateQrCode(QRCoder.PayloadGenerator.Payload payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel) { } @@ -878,6 +878,7 @@ namespace QRCoder.Extensions { public static string? GetStringValue(this System.Enum value) { } } + [System.AttributeUsage(System.AttributeTargets.Field)] [System.Obsolete("This attribute will be removed in a future version of QRCoder.")] public class StringValueAttribute : System.Attribute { diff --git a/QRCoderBenchmarks/QRCoderBenchmarks.csproj b/QRCoderBenchmarks/QRCoderBenchmarks.csproj index 323afbdb..08fa5c6d 100644 --- a/QRCoderBenchmarks/QRCoderBenchmarks.csproj +++ b/QRCoderBenchmarks/QRCoderBenchmarks.csproj @@ -5,6 +5,7 @@ net8.0 enable enable + $(NoWarn);CA1822 diff --git a/QRCoderConsole/Program.cs b/QRCoderConsole/Program.cs index d6e82c54..7b8dfd04 100644 --- a/QRCoderConsole/Program.cs +++ b/QRCoderConsole/Program.cs @@ -16,7 +16,6 @@ public static void Main(string[] args) { var friendlyName = AppDomain.CurrentDomain.FriendlyName; var newLine = Environment.NewLine; - var setter = new OptionSetter(); string? fileName = null, outputFileName = null, payload = null; @@ -33,7 +32,7 @@ public static void Main(string[] args) var optionSet = new OptionSet { { "e|ecc-level=", "error correction level", - value => eccLevel = setter.GetECCLevel(value) + value => eccLevel = OptionSetter.GetECCLevel(value) }, { "f|output-format=", $"Image format for outputfile. Possible values: {string.Join(", ", Enum.GetNames(typeof(SupportedImageFormat)))} (default: png)", @@ -146,7 +145,7 @@ private static void GenerateQRCode(string payloadString, QRCodeGenerator.ECCLeve using (var code = new QRCode(data)) { using var bitmap = code.GetGraphic(pixelsPerModule, foreground, background, true); - var actualFormat = new OptionSetter().GetImageFormat(imgFormat.ToString()); + var actualFormat = OptionSetter.GetImageFormat(imgFormat.ToString()); bitmap.Save(outputFileName, actualFormat); } break; @@ -223,20 +222,15 @@ private static void ShowHelp(OptionSet optionSet) } } -public class OptionSetter +public static class OptionSetter { - public QRCodeGenerator.ECCLevel GetECCLevel(string value) - { - - Enum.TryParse(value, out QRCodeGenerator.ECCLevel level); - - return level; - } + public static QRCodeGenerator.ECCLevel GetECCLevel(string value) + => Enum.TryParse(value, true, out QRCodeGenerator.ECCLevel level) ? level : QRCodeGenerator.ECCLevel.Default; #if NET6_0 && WINDOWS [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public ImageFormat GetImageFormat(string value) => value.ToLower() switch + public static ImageFormat GetImageFormat(string value) => value.ToLowerInvariant() switch { "jpg" => ImageFormat.Jpeg, "jpeg" => ImageFormat.Jpeg, diff --git a/QRCoderDemo/QRCoderDemo.csproj b/QRCoderDemo/QRCoderDemo.csproj index f6530adf..503e7923 100644 --- a/QRCoderDemo/QRCoderDemo.csproj +++ b/QRCoderDemo/QRCoderDemo.csproj @@ -5,6 +5,7 @@ true Exe true + en-us diff --git a/QRCoderTests/ArtQRCodeRendererTests.cs b/QRCoderTests/ArtQRCodeRendererTests.cs index 349f3bb7..96c1c845 100644 --- a/QRCoderTests/ArtQRCodeRendererTests.cs +++ b/QRCoderTests/ArtQRCodeRendererTests.cs @@ -53,8 +53,8 @@ public void should_throw_pixelfactor_oor_exception() var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); var aCode = new ArtQRCode(data); - var exception = Should.Throw(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); - exception.Message.ShouldBe("The parameter pixelSize must be between 0 and 1. (0-100%)"); + var exception = Should.Throw(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); + exception.Message.ShouldStartWith("The parameter pixelSizeFactor must be between 0 and 1. (0-100%)"); } [Fact] diff --git a/QRCoderTests/Helpers/HelperFunctions.cs b/QRCoderTests/Helpers/HelperFunctions.cs index 4e928093..d848fc0b 100644 --- a/QRCoderTests/Helpers/HelperFunctions.cs +++ b/QRCoderTests/Helpers/HelperFunctions.cs @@ -43,47 +43,6 @@ public static string GetAssemblyPath() => Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)?.Replace("file:\\", "") ?? ""; #endif - /// - /// Converts a bitmap to a hash string based on the pixel data - /// using a deterministic algorithm that ignores compression algorithm - /// differences across platforms. - /// - public static string BitmapToHash(Bitmap bitmap) - { - // Lock the bitmap's bits. - var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); - var bitmapData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - byte[] rgbValues; - try - { - // Create an array to hold the bytes of the bitmap. - int bytes = Math.Abs(bitmapData.Stride) * bitmap.Height; - rgbValues = new byte[bytes]; - - // Copy the RGB values into the array. - System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, rgbValues, 0, bytes); - } - finally - { - // Unlock the bits. - bitmap.UnlockBits(bitmapData); - } - - // Hash the resulting byte array - return ByteArrayToHash(rgbValues); - } - - public static string ByteArrayToHash(byte[] data) - { - var md5 = MD5.Create(); - var hash = md5.ComputeHash(data); - return BitConverter.ToString(hash).Replace("-", "").ToLower(); - } - - public static string StringToHash(string data) - => ByteArrayToHash(Encoding.UTF8.GetBytes(data)); - /// /// Gets the embedded PNG icon as a Bitmap. /// diff --git a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs index d6f6ee66..171c4761 100644 --- a/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs +++ b/QRCoderTests/Helpers/ShouldMatchApprovedBinaryExtensions.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.ComponentModel; using System.Diagnostics; using System.IO; @@ -59,19 +57,19 @@ public static void ShouldMatchApproved(this byte[] actual, string fileExtension, } if (frame == null) - throw new Exception("Unable to get stack frame information"); + throw new ShouldAssertException("Unable to get stack frame information"); if (method == null) - throw new Exception("Unable to get method information from stack frame"); + throw new ShouldAssertException("Unable to get method information from stack frame"); var fileName = frame.GetFileName(); if (string.IsNullOrEmpty(fileName)) - throw new Exception($"Source information not available, make sure you are compiling with full debug information. Method: {method.DeclaringType?.Name}.{method.Name}"); + throw new ShouldAssertException($"Source information not available, make sure you are compiling with full debug information. Method: {method.DeclaringType?.Name}.{method.Name}"); var outputFolder = Path.GetDirectoryName(fileName); if (string.IsNullOrEmpty(outputFolder)) - throw new Exception($"Unable to determine output folder from file: {fileName}"); + throw new ShouldAssertException($"Unable to determine output folder from file: {fileName}"); - var className = method.DeclaringType?.Name ?? throw new Exception("Unable to get class name from method information"); + var className = method.DeclaringType?.Name ?? throw new ShouldAssertException("Unable to get class name from method information"); var testMethodName = method.Name; var discriminatorPart = string.IsNullOrEmpty(discriminator) ? "" : $".{discriminator}"; diff --git a/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs b/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs index 7109dda0..6ef513c4 100644 --- a/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/BezahlCodeTests.cs @@ -11,12 +11,12 @@ public void bezahlcode_generator_can_generate_payload_singlepayment_minimal() var bnc = "100205000"; var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -30,12 +30,12 @@ public void bezahlcode_generator_can_generate_payload_singlepayment_full() var amount = 10.00m; var postingKey = 69; var currency = Currency.USD; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, DateTime.Now); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -49,12 +49,12 @@ public void bezahlcode_generator_can_generate_payload_singledirectdebit() var amount = 10.00m; var postingKey = 69; var currency = Currency.USD; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebit, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, DateTime.Now); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://singledirectdebit?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singledirectdebit?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -72,12 +72,12 @@ public void bezahlcode_generator_can_generate_payload_periodicsinglepayment() var periodicFirstExecutionDate = DateTime.Now; var periodicLastExecutionDate = DateTime.Now.AddMonths(3); var currency = Currency.USD; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://periodicsinglepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + "&periodictimeunit=W&periodictimeunitrotation=2&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy") + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy")); + .ShouldBe("bank://periodicsinglepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + "&periodictimeunit=W&periodictimeunitrotation=2&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)); } @@ -93,7 +93,7 @@ public void bezahlcode_generator_can_generate_payload_singlepaymentsepa_minimal( generator .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -112,7 +112,7 @@ public void bezahlcode_generator_can_generate_payload_singlepaymentsepa_full() generator .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -133,7 +133,7 @@ public void bezahlcode_generator_can_generate_payload_singledirectdebitsepa() generator .ToString() - .ShouldBe("bank://singledirectdebitsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&creditorid=DE%2002%20TSV%2001234567890&mandateid=987543CB2&dateofsignature=01032017&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singledirectdebitsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&creditorid=DE%2002%20TSV%2001234567890&mandateid=987543CB2&dateofsignature=01032017&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -156,7 +156,7 @@ public void bezahlcode_generator_can_generate_payload_periodicsinglepaymentsepa( generator .ToString() - .ShouldBe("bank://periodicsinglepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + "&periodictimeunit=M&periodictimeunitrotation=1&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy") + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy")); + .ShouldBe("bank://periodicsinglepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + "&periodictimeunit=M&periodictimeunitrotation=1&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)); } @@ -245,12 +245,12 @@ public void bezahlcode_generator_should_handle_account_whitespaces() var bnc = "100205000"; var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=01194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=01194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -261,12 +261,12 @@ public void bezahlcode_generator_should_handle_bnc_whitespaces() var bnc = "10020 5000"; var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -282,7 +282,7 @@ public void bezahlcode_generator_should_handle_iban_whitespaces() generator .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -298,7 +298,7 @@ public void bezahlcode_generator_should_handle_bic_whitespaces() generator .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -309,12 +309,12 @@ public void bezahlcode_generator_should_add_decimals() var bnc = "10020 5000"; var name = "Wikimedia Fördergesellschaft"; var amount = 10; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); -#pragma warning restore CS0612 +#pragma warning restore CS0618 generator .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy", CultureInfo.InvariantCulture) + ""); } @@ -324,9 +324,9 @@ public void bezahlcode_generator_should_throw_wrong_contact_constructor_exceptio var account = "0001194700"; var bnc = "10020 5000"; var name = "Wikimedia Fördergesellschaft"; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, "", "", "New business contact.")); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); } @@ -371,9 +371,9 @@ public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_periodic var periodicFirstExecutionDate = DateTime.Now; var periodicLastExecutionDate = DateTime.Now.AddMonths(3); var currency = Currency.USD; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now)); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); } @@ -385,9 +385,9 @@ public void bezahlcode_generator_should_throw_wrong_sepa_constructor_exception() var bic = "BFSWDE33BER"; var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, iban: iban, bic: bic, amount: amount)); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); } @@ -452,9 +452,9 @@ public void bezahlcode_generator_should_throw_invalid_account_exception() var bnc = "100205000"; var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("The account entered isn't valid."); } @@ -466,9 +466,9 @@ public void bezahlcode_generator_should_throw_invalid_bnc_exception() var bnc = "10020500023545626226262"; var name = "Wikimedia Fördergesellschaft"; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("The bnc entered isn't valid."); } @@ -481,9 +481,9 @@ public void bezahlcode_generator_should_throw_invalid_postingkey_exception() var name = "Wikimedia Fördergesellschaft"; var postingKey = 101; var amount = 10.00m; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount, postingKey: postingKey)); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("PostingKey must be within 0 and 99."); } @@ -616,9 +616,9 @@ public void bezahlcode_generator_should_throw_invalid_executiondate_exception() var postingKey = 69; var executionDate = new DateTime(2017, 1, 1); var currency = Currency.USD; -#pragma warning disable CS0612 +#pragma warning disable CS0618 var exception = Should.Throw(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, executionDate)); -#pragma warning restore CS0612 +#pragma warning restore CS0618 exception.Message.ShouldBe("Execution date must be today or in future."); } diff --git a/QRCoderTests/QRCoderTests.csproj b/QRCoderTests/QRCoderTests.csproj index b09c6e1e..aec5434f 100644 --- a/QRCoderTests/QRCoderTests.csproj +++ b/QRCoderTests/QRCoderTests.csproj @@ -1,5 +1,5 @@  - + net462;netcoreapp2.1;netcoreapp3.1;net5.0-windows;net6.0-windows true @@ -7,8 +7,9 @@ $(DefineConstants);TEST_XAML true 2.1.30 + $(NoWarn);CA1707;CA1416 - + @@ -32,25 +33,25 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + - + - + - + - + $([System.String]::Copy('%(Filename)').Split('.')[0]).cs diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index 62f7f0e1..e1de26ab 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA5350 // Weak cryptography algorithm (SHA1). + using System.Reflection; using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel;