From c52c670691be1a0e356d9003d1b5a5fe76b0a318 Mon Sep 17 00:00:00 2001 From: Wilson Mun <30316250+wmundev@users.noreply.github.com> Date: Thu, 29 Oct 2026 00:06:07 +1100 Subject: [PATCH] fix: match Java vanity normalization for line terminators --- .../PhoneNumbers.Test/TestPhoneNumberUtil.cs | 24 +++++++++++++++++++ csharp/PhoneNumbers/PhoneNumberUtil.cs | 8 +++++++ csharp/PhoneNumbers/PhoneNumberUtil.net.cs | 3 ++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/csharp/PhoneNumbers.Test/TestPhoneNumberUtil.cs b/csharp/PhoneNumbers.Test/TestPhoneNumberUtil.cs index 418264e9..d457939b 100644 --- a/csharp/PhoneNumbers.Test/TestPhoneNumberUtil.cs +++ b/csharp/PhoneNumbers.Test/TestPhoneNumberUtil.cs @@ -471,6 +471,30 @@ public void TestNormaliseReplaceAlphaCharacters() PhoneNumberUtil.Normalize(inputNumber)); } + [Fact] + public void TestNormaliseStripAlphaCharactersWithLineTerminators() + { + // Java's VALID_ALPHA_PHONE_PATTERN uses dot without DOTALL and matches the whole input. + foreach (var separator in new[] { "\n", "\r", "\r\n", "\u0085", "\u2028", "\u2029" }) + { + foreach (var input in new[] + { + separator + "1800FLOWERS", + "1800F" + separator + "LOWERS", + "1800FLOWERS" + separator, + }) + { + Assert.Equal("1800", PhoneNumberUtil.Normalize(input)); + } + } + + // Other whitespace is accepted by Java's dot and must not disable vanity conversion. + foreach (var separator in new[] { " ", "\t", "\v", "\f" }) + { + Assert.Equal("18003569377", PhoneNumberUtil.Normalize("1800F" + separator + "LOWERS")); + } + } + [Fact] public void TestNormaliseOtherDigits() { diff --git a/csharp/PhoneNumbers/PhoneNumberUtil.cs b/csharp/PhoneNumbers/PhoneNumberUtil.cs index 66bd1e38..deb3177f 100644 --- a/csharp/PhoneNumbers/PhoneNumberUtil.cs +++ b/csharp/PhoneNumbers/PhoneNumberUtil.cs @@ -209,7 +209,15 @@ private static bool IsValidAlphaPhone(StringBuilder number) { var lower = number[i] | 0x20; if ((uint)(lower - 'a') <= 'z' - 'a' && ++alpha == 3) + { + // Java's normalization pattern matches the whole input without DOTALL. + for (var j = 0; j < number.Length; j++) + { + if (number[j] is '\n' or '\r' or '\u0085' or '\u2028' or '\u2029') + return false; + } return true; + } } return false; } diff --git a/csharp/PhoneNumbers/PhoneNumberUtil.net.cs b/csharp/PhoneNumbers/PhoneNumberUtil.net.cs index 1bc6a737..7613f6cc 100644 --- a/csharp/PhoneNumbers/PhoneNumberUtil.net.cs +++ b/csharp/PhoneNumbers/PhoneNumberUtil.net.cs @@ -663,7 +663,8 @@ private static bool IsValidAlphaPhone(string number) var lower = number[i] | 0x20; if ((uint)(lower - 'a') <= 'z' - 'a' && ++alpha == 3) { - return true; + // Java's normalization pattern matches the whole input without DOTALL. + return number.AsSpan().IndexOfAny("\n\r\u0085\u2028\u2029") < 0; } }