/** * Parses a string and returns it in proto buffer format. This method differs from {@link #parse} * in that it always populates the raw_input field of the protocol buffer with numberToParse as * well as the country_code_source field. * * @param numberToParse number that we are attempting to parse. This can contain formatting * such as +, ( and -, as well as a phone number extension. * @param defaultRegion region that we are expecting the number to be from. This is only used * if the number being parsed is not written in international format. * The country calling code for the number in this case would be stored * as that of the default region supplied. * @return a phone number proto buffer filled with the parsed number * @throws NumberParseException if the string is not considered to be a viable phone number or if * no default region was supplied */ public PhoneNumber ParseAndKeepRawInput(String numberToParse, String defaultRegion) { var phoneNumber = new PhoneNumber.Builder(); ParseAndKeepRawInput(numberToParse, defaultRegion, phoneNumber); return phoneNumber.Build(); }
/** * Takes two phone numbers and compares them for equality. This is a convenience wrapper for * {@link #isNumberMatch(PhoneNumber, PhoneNumber)}. No default region is known. * * @param firstNumber first number to compare in proto buffer format. * @param secondNumber second number to compare. Can contain formatting, and can have country * calling code specified with + at the start. * @return NOT_A_NUMBER, NO_MATCH, SHORT_NSN_MATCH, NSN_MATCH, EXACT_MATCH. See * {@link #isNumberMatch(PhoneNumber, PhoneNumber)} for more details. */ public MatchType IsNumberMatch(PhoneNumber firstNumber, String secondNumber) { // First see if the second number has an implicit country calling code, by attempting to parse // it. try { PhoneNumber secondNumberAsProto = Parse(secondNumber, UNKNOWN_REGION); return IsNumberMatch(firstNumber, secondNumberAsProto); } catch (NumberParseException e) { if (e.ErrorType == ErrorType.INVALID_COUNTRY_CODE) { // The second number has no country calling code. EXACT_MATCH is no longer possible. // We parse it as if the region was the same as that for the first number, and if // EXACT_MATCH is returned, we replace this with NSN_MATCH. String firstNumberRegion = GetRegionCodeForCountryCode(firstNumber.CountryCode); try { if (!firstNumberRegion.Equals(UNKNOWN_REGION)) { PhoneNumber secondNumberWithFirstNumberRegion = Parse(secondNumber, firstNumberRegion); MatchType match = IsNumberMatch(firstNumber, secondNumberWithFirstNumberRegion); if (match == MatchType.EXACT_MATCH) return MatchType.NSN_MATCH; return match; } else { // If the first number didn't have a valid country calling code, then we parse the // second number without one as well. var secondNumberProto = new PhoneNumber.Builder(); ParseHelper(secondNumber, null, false, false, secondNumberProto); return IsNumberMatch(firstNumber, secondNumberProto.Build()); } } catch (NumberParseException) { // Fall-through to return NOT_A_NUMBER. } } } // One or more of the phone numbers we are trying to match is not a viable phone number. return MatchType.NOT_A_NUMBER; }
/** * Takes two phone numbers as strings and compares them for equality. This is a convenience * wrapper for {@link #isNumberMatch(PhoneNumber, PhoneNumber)}. No default region is known. * * @param firstNumber first number to compare. Can contain formatting, and can have country * calling code specified with + at the start. * @param secondNumber second number to compare. Can contain formatting, and can have country * calling code specified with + at the start. * @return NOT_A_NUMBER, NO_MATCH, SHORT_NSN_MATCH, NSN_MATCH, EXACT_MATCH. See * {@link #isNumberMatch(PhoneNumber, PhoneNumber)} for more details. */ public MatchType IsNumberMatch(String firstNumber, String secondNumber) { try { PhoneNumber firstNumberAsProto = Parse(firstNumber, UNKNOWN_REGION); return IsNumberMatch(firstNumberAsProto, secondNumber); } catch (NumberParseException e) { if (e.ErrorType == ErrorType.INVALID_COUNTRY_CODE) { try { PhoneNumber secondNumberAsProto = Parse(secondNumber, UNKNOWN_REGION); return IsNumberMatch(secondNumberAsProto, firstNumber); } catch (NumberParseException e2) { if (e2.ErrorType == ErrorType.INVALID_COUNTRY_CODE) { try { var firstNumberProto = new PhoneNumber.Builder(); var secondNumberProto = new PhoneNumber.Builder(); ParseHelper(firstNumber, null, false, false, firstNumberProto); ParseHelper(secondNumber, null, false, false, secondNumberProto); return IsNumberMatch(firstNumberProto.Build(), secondNumberProto.Build()); } catch (NumberParseException) { // Fall through and return MatchType.NOT_A_NUMBER. } } } } } // One or more of the phone numbers we are trying to match is not a viable phone number. return MatchType.NOT_A_NUMBER; }
/** * Takes two phone numbers and compares them for equality. * * <p>Returns EXACT_MATCH if the country_code, NSN, presence of a leading zero for Italian numbers * and any extension present are the same. * Returns NSN_MATCH if either or both has no region specified, and the NSNs and extensions are * the same. * Returns SHORT_NSN_MATCH if either or both has no region specified, or the region specified is * the same, and one NSN could be a shorter version of the other number. This includes the case * where one has an extension specified, and the other does not. * Returns NO_MATCH otherwise. * For example, the numbers +1 345 657 1234 and 657 1234 are a SHORT_NSN_MATCH. * The numbers +1 345 657 1234 and 345 657 are a NO_MATCH. * * @param firstNumberIn first number to compare * @param secondNumberIn second number to compare * * @return NO_MATCH, SHORT_NSN_MATCH, NSN_MATCH or EXACT_MATCH depending on the level of equality * of the two numbers, described in the method definition. */ public MatchType IsNumberMatch(PhoneNumber firstNumberIn, PhoneNumber secondNumberIn) { // Make copies of the phone number so that the numbers passed in are not edited. var firstNumber = new PhoneNumber.Builder(); firstNumber.MergeFrom(firstNumberIn); var secondNumber = new PhoneNumber.Builder(); secondNumber.MergeFrom(secondNumberIn); // First clear raw_input, country_code_source and preferred_domestic_carrier_code fields and any // empty-string extensions so that we can use the proto-buffer equality method. firstNumber.ClearRawInput(); firstNumber.ClearCountryCodeSource(); firstNumber.ClearPreferredDomesticCarrierCode(); secondNumber.ClearRawInput(); secondNumber.ClearCountryCodeSource(); secondNumber.ClearPreferredDomesticCarrierCode(); if (firstNumber.HasExtension && firstNumber.Extension.Length == 0) firstNumber.ClearExtension(); if (secondNumber.HasExtension && secondNumber.Extension.Length == 0) secondNumber.ClearExtension(); // Early exit if both had extensions and these are different. if (firstNumber.HasExtension && secondNumber.HasExtension && !firstNumber.Extension.Equals(secondNumber.Extension)) return MatchType.NO_MATCH; int firstNumberCountryCode = firstNumber.CountryCode; int secondNumberCountryCode = secondNumber.CountryCode; // Both had country_code specified. if (firstNumberCountryCode != 0 && secondNumberCountryCode != 0) { if (AreEqual(firstNumber, secondNumber)) return MatchType.EXACT_MATCH; else if (firstNumberCountryCode == secondNumberCountryCode && IsNationalNumberSuffixOfTheOther(firstNumber, secondNumber)) { // A SHORT_NSN_MATCH occurs if there is a difference because of the presence or absence of // an 'Italian leading zero', the presence or absence of an extension, or one NSN being a // shorter variant of the other. return MatchType.SHORT_NSN_MATCH; } // This is not a match. return MatchType.NO_MATCH; } // Checks cases where one or both country_code fields were not specified. To make equality // checks easier, we first set the country_code fields to be equal. firstNumber.SetCountryCode(secondNumberCountryCode); // If all else was the same, then this is an NSN_MATCH. if (AreEqual(firstNumber, secondNumber)) return MatchType.NSN_MATCH; if (IsNationalNumberSuffixOfTheOther(firstNumber, secondNumber)) return MatchType.SHORT_NSN_MATCH; return MatchType.NO_MATCH; }
/** * Gets the length of the national destination code (NDC) from the PhoneNumber object passed in, * so that clients could use it to split a national significant number into NDC and subscriber * number. The NDC of a phone number is normally the first group of digit(s) right after the * country calling code when the number is formatted in the international format, if there is a * subscriber number part that follows. An example of how this could be used: * * <pre> * PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); * PhoneNumber number = phoneUtil.parse("18002530000", "US"); * String nationalSignificantNumber = phoneUtil.getNationalSignificantNumber(number); * String nationalDestinationCode; * String subscriberNumber; * * int nationalDestinationCodeLength = phoneUtil.getLengthOfNationalDestinationCode(number); * if (nationalDestinationCodeLength > 0) { * nationalDestinationCode = nationalSignificantNumber.substring(0, * nationalDestinationCodeLength); * subscriberNumber = nationalSignificantNumber.substring(nationalDestinationCodeLength); * } else { * nationalDestinationCode = ""; * subscriberNumber = nationalSignificantNumber; * } * </pre> * * Refer to the unittests to see the difference between this function and * {@link #getLengthOfGeographicalAreaCode}. * * @param number the PhoneNumber object for which clients want to know the length of the NDC. * @return the length of NDC of the PhoneNumber object passed in. */ public int GetLengthOfNationalDestinationCode(PhoneNumber number) { PhoneNumber copiedProto; if (number.HasExtension) { // We don't want to alter the proto given to us, but we don't want to include the extension // when we format it, so we copy it and clear the extension here. var builder = new PhoneNumber.Builder(); builder.MergeFrom(number); builder.ClearExtension(); copiedProto = builder.Build(); } else { copiedProto = number; } var nationalSignificantNumber = Format(copiedProto, PhoneNumberFormat.INTERNATIONAL); var numberGroups = NON_DIGITS_PATTERN.Split(nationalSignificantNumber); // The pattern will start with "+COUNTRY_CODE " so the first group will always be the empty // string (before the + symbol) and the second group will be the country calling code. The third // group will be area code if it is not the last group. if (numberGroups.Length <= 3) return 0; if (GetRegionCodeForCountryCode(number.CountryCode) == "AR" && GetNumberType(number) == PhoneNumberType.MOBILE) // Argentinian mobile numbers, when formatted in the international format, are in the form of // +54 9 NDC XXXX.... As a result, we take the length of the third group (NDC) and add 1 for // the digit 9, which also forms part of the national significant number. // // TODO: Investigate the possibility of better modeling the metadata to make it // easier to obtain the NDC. return numberGroups[3].Length + 1; return numberGroups[2].Length; }
/** * Returns a number formatted in such a way that it can be dialed from a mobile phone in a * specific region. If the number cannot be reached from the region (e.g. some countries block * toll-free numbers from being called outside of the country), the method returns an empty * string. * * @param number the phone number to be formatted * @param regionCallingFrom the region where the call is being placed * @param withFormatting whether the number should be returned with formatting symbols, such as * spaces and dashes. * @return the formatted phone number */ public String FormatNumberForMobileDialing(PhoneNumber number, String regionCallingFrom, bool withFormatting) { int countryCallingCode = number.CountryCode; if (!HasValidCountryCallingCode(countryCallingCode)) { return number.HasRawInput ? number.RawInput : ""; } String formattedNumber; // Clear the extension, as that part cannot normally be dialed together with the main number. PhoneNumber numberNoExt = new PhoneNumber.Builder().MergeFrom(number).ClearExtension().Build(); PhoneNumberType numberType = GetNumberType(numberNoExt); String regionCode = GetRegionCodeForCountryCode(countryCallingCode); if (regionCode.Equals("CO") && regionCallingFrom.Equals("CO")) { if (numberType == PhoneNumberType.FIXED_LINE) { formattedNumber = FormatNationalNumberWithCarrierCode(numberNoExt, COLOMBIA_MOBILE_TO_FIXED_LINE_PREFIX); } else { // E164 doesn't work at all when dialing within Colombia. formattedNumber = Format(numberNoExt, PhoneNumberFormat.NATIONAL); } } else if (regionCode.Equals("PE") && regionCallingFrom.Equals("PE")) { // In Peru, numbers cannot be dialled using E164 format from a mobile phone for Movistar. // Instead they must be dialled in national format. formattedNumber = Format(numberNoExt, PhoneNumberFormat.NATIONAL); } else if (regionCode.Equals("BR") && regionCallingFrom.Equals("BR") && ((numberType == PhoneNumberType.FIXED_LINE) || (numberType == PhoneNumberType.MOBILE) || (numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE))) { formattedNumber = numberNoExt.HasPreferredDomesticCarrierCode ? FormatNationalNumberWithPreferredCarrierCode(numberNoExt, "") // Brazilian fixed line and mobile numbers need to be dialed with a carrier code when // called within Brazil. Without that, most of the carriers won't connect the call. // Because of that, we return an empty string here. : ""; } else if (CanBeInternationallyDialled(numberNoExt)) { return withFormatting ? Format(numberNoExt, PhoneNumberFormat.INTERNATIONAL) : Format(numberNoExt, PhoneNumberFormat.E164); } else { formattedNumber = (regionCallingFrom == regionCode) ? Format(numberNoExt, PhoneNumberFormat.NATIONAL) : ""; } return withFormatting ? formattedNumber : NormalizeHelper(formattedNumber, DIALLABLE_CHAR_MAPPINGS, true /* remove non matches */); }