/** * Sets the possible length fields in the metadata from the sets of data passed in. Checks that * the length is covered by the "parent" phone number description element if one is present, and * if the lengths are exactly the same as this, they are not filled in for efficiency reasons. * * @param parentDesc the "general description" element or null if desc is the generalDesc itself * @param desc the PhoneNumberDesc object that we are going to set lengths for */ private static void SetPossibleLengths(SortedSet<int> lengths, SortedSet<int> localOnlyLengths, PhoneNumberDesc parentDesc, PhoneNumberDesc.Builder desc) { // Only add the lengths to this sub-type if they aren't exactly the same as the possible // lengths in the general desc (for metadata size reasons). if (parentDesc == null || !ArePossibleLengthsEqual(lengths, parentDesc)) foreach (var length in lengths) if (parentDesc == null || parentDesc.PossibleLengthList.Contains(length)) desc.PossibleLengthList.Add(length); else throw new Exception( #if NET35 $"Out-of-range possible length found ({length}), parent lengths {string.Join(", ", parentDesc.PossibleLengthList.Select(x => x.ToString()).ToArray())}."); #else $"Out-of-range possible length found ({length}), parent lengths {string.Join(", ", parentDesc.PossibleLengthList)}."); #endif // We check that the local-only length isn't also a normal possible length (only relevant for // the general-desc, since within elements such as fixed-line we would throw an exception if we // saw this) before adding it to the collection of possible local-only lengths. foreach (var length in localOnlyLengths) if (!lengths.Contains(length)) if (parentDesc == null || parentDesc.PossibleLengthLocalOnlyList.Contains(length) || parentDesc.PossibleLengthList.Contains(length)) desc.PossibleLengthLocalOnlyList.Add(length); else throw new Exception( #if NET35 $"Out-of-range local-only possible length found ({length}), parent length {string.Join(", ", parentDesc.PossibleLengthLocalOnlyList.Select(x => x.ToString()).ToArray())}."); #else $"Out-of-range local-only possible length found ({length}), parent length {string.Join(", ", parentDesc.PossibleLengthLocalOnlyList)}."); #endif }
/// <summary> /// Gets a valid short number for the specified cost category. /// </summary> /// /// <param name="regionCode">the region for which an example short number is needed</param> /// <param name="cost">the cost category of number that is needed</param> /// <returns> a valid short number for the specified region and cost category. Returns an empty /// string when the metadata does not contain such information, or the cost is UNKNOWN_COST.</returns> internal string GetExampleShortNumberForCost(string regionCode, ShortNumberCost cost) { var phoneMetadata = MetadataManager.GetShortNumberMetadataForRegion(regionCode); if (phoneMetadata == null) { return(""); } PhoneNumberDesc desc = null; switch (cost) { case ShortNumberCost.TOLL_FREE: desc = phoneMetadata.TollFree; break; case ShortNumberCost.STANDARD_RATE: desc = phoneMetadata.StandardRate; break; case ShortNumberCost.PREMIUM_RATE: desc = phoneMetadata.PremiumRate; break; // UNKNOWN_COST numbers are computed by the process of elimination from the other cost // categories. } return(desc?.ExampleNumber ?? string.Empty); }
// TODO: Once we have benchmarked ShortNumberInfo, consider if it is worth keeping // this performance optimization. private bool MatchesPossibleNumberAndNationalNumber(String number, PhoneNumberDesc numberDesc) { if (numberDesc.PossibleLengthCount > 0 && !numberDesc.PossibleLengthList.Contains(number.Length)) { return(false); } return(matcherApi.matchNationalNumber(number, numberDesc, false)); }
/** * Processes a phone number description element from the XML file and returns it as a * PhoneNumberDesc. If the description element is a fixed line or mobile number, the parent * description will be used to fill in the whole element if necessary, or any components that are * missing. For all other types, the parent description will only be used to fill in missing * components if the type has a partial definition. For example, if no "tollFree" element exists, * we assume there are no toll free numbers for that locale, and return a phone number description * with "NA" for both the national and possible number patterns. * * @param generalDesc a generic phone number description that will be used to fill in missing * parts of the description * @param countryElement the XML element representing all the country information * @param numberType the name of the number type, corresponding to the appropriate tag in the XML * file with information about that type * @return complete description of that phone number type */ public static PhoneNumberDesc.Builder ProcessPhoneNumberDescElement(PhoneNumberDesc parentDesc, XElement countryElement, string numberType) { if (parentDesc == null) { parentDesc = new PhoneNumberDesc.Builder().Build(); } var phoneNumberDescList = countryElement.GetElementsByTagName(numberType).ToList(); var numberDesc = new PhoneNumberDesc.Builder(); if (phoneNumberDescList.Count == 0) { // -1 will never match a possible phone number length, so is safe to use to ensure this never // matches. We don't leave it empty, since for compression reasons, we use the empty list to // mean that the generalDesc possible lengths apply. numberDesc.AddPossibleLength(-1); return(numberDesc); } if (phoneNumberDescList.Count > 0) { if (phoneNumberDescList.Count > 1) { throw new Exception($"Multiple elements with type {numberType} found."); } var element = phoneNumberDescList[0]; if (parentDesc != null) { // New way of handling possible number lengths. We don't do this for the general // description, since these tags won't be present; instead we will calculate its values // based on the values for all the other number type descriptions (see // setPossibleLengthsGeneralDesc). var lengths = new SortedSet <int>(); var localOnlyLengths = new SortedSet <int>(); PopulatePossibleLengthSets(element, lengths, localOnlyLengths); SetPossibleLengths(lengths, new SortedSet <int>(), parentDesc, numberDesc); } var validPattern = element.GetElementsByTagName(NATIONAL_NUMBER_PATTERN).ToList(); if (validPattern.Any()) { numberDesc.SetNationalNumberPattern(ValidateRE(validPattern.First().Value, true)); } var exampleNumber = element.GetElementsByTagName(EXAMPLE_NUMBER).ToList(); if (exampleNumber.Any()) { numberDesc.SetExampleNumber(exampleNumber.First().Value); } } return(numberDesc); }
/** * Checks if the possible lengths provided as a sorted set are equal to the possible lengths * stored already in the description pattern. Note that possibleLengths may be empty but must not * be null, and the PhoneNumberDesc passed in should also not be null. */ private static bool ArePossibleLengthsEqual(SortedSet<int> possibleLengths, PhoneNumberDesc desc) { if (possibleLengths.Count != desc.PossibleLengthCount) return false; // Note that both should be sorted already, and we know they are the same length. var i = 0; foreach (var length in possibleLengths) { if (length != desc.PossibleLengthList[i]) return false; i++; } return true; }
/** * Sets the possible length fields in the metadata from the sets of data passed in. Checks that * the length is covered by the "parent" phone number description element if one is present, and * if the lengths are exactly the same as this, they are not filled in for efficiency reasons. * * @param parentDesc the "general description" element or null if desc is the generalDesc itself * @param desc the PhoneNumberDesc object that we are going to set lengths for */ private static void SetPossibleLengths(SortedSet <int> lengths, SortedSet <int> localOnlyLengths, PhoneNumberDesc parentDesc, PhoneNumberDesc.Builder desc) { // Only add the lengths to this sub-type if they aren't exactly the same as the possible // lengths in the general desc (for metadata size reasons). if (parentDesc == null || !ArePossibleLengthsEqual(lengths, parentDesc)) { foreach (var length in lengths) { if (parentDesc == null || parentDesc.PossibleLengthList.Contains(length)) { desc.PossibleLengthList.Add(length); } else { // We shouldn't have possible lengths defined in a child element that are not covered by // the general description. We check this here even though the general description is // derived from child elements because it is only derived from a subset, and we need to // ensure *all* child elements have a valid possible length. throw new Exception( $"Out-of-range possible length found ({length}), parent lengths {string.Join(", ", parentDesc.PossibleLengthList)}."); } } } // We check that the local-only length isn't also a normal possible length (only relevant for // the general-desc, since within elements such as fixed-line we would throw an exception if we // saw this) before adding it to the collection of possible local-only lengths. foreach (var length in localOnlyLengths) { if (!lengths.Contains(length)) { // We check it is covered by either of the possible length sets of the parent // PhoneNumberDesc, because for example 7 might be a valid localOnly length for mobile, but // a valid national length for fixedLine, so the generalDesc would have the 7 removed from // localOnly. if (parentDesc == null || parentDesc.PossibleLengthLocalOnlyList.Contains(length) || parentDesc.PossibleLengthList.Contains(length)) { desc.PossibleLengthLocalOnlyList.Add(length); } else { throw new Exception( $"Out-of-range local-only possible length found ({length}), parent length {string.Join(", ", parentDesc.PossibleLengthLocalOnlyList)}."); } } } }
/** * Processes a phone number description element from the XML file and returns it as a * PhoneNumberDesc. If the description element is a fixed line or mobile number, the general * description will be used to fill in the whole element if necessary, or any components that are * missing. For all other types, the general description will only be used to fill in missing * components if the type has a partial definition. For example, if no "tollFree" element exists, * we assume there are no toll free numbers for that locale, and return a phone number description * with "NA" for both the national and possible number patterns. * * @param generalDesc a generic phone number description that will be used to fill in missing * parts of the description * @param countryElement the XML element representing all the country information * @param numberType the name of the number type, corresponding to the appropriate tag in the XML * file with information about that type * @return complete description of that phone number type */ public static PhoneNumberDesc ProcessPhoneNumberDescElement(PhoneNumberDesc generalDesc, XElement countryElement, String numberType, bool liteBuild) { if (generalDesc == null) { generalDesc = new PhoneNumberDesc.Builder().Build(); } var phoneNumberDescList = countryElement.GetElementsByTagName(numberType); var numberDesc = new PhoneNumberDesc.Builder(); if (phoneNumberDescList.Length == 0 && !IsValidNumberType(numberType)) { numberDesc.SetNationalNumberPattern("NA"); numberDesc.SetPossibleNumberPattern("NA"); return(numberDesc.Build()); } numberDesc.MergeFrom(generalDesc); if (phoneNumberDescList.Length > 0) { XElement element = phoneNumberDescList[0]; var possiblePattern = element.GetElementsByTagName(POSSIBLE_NUMBER_PATTERN); if (possiblePattern.Length > 0) { numberDesc.SetPossibleNumberPattern(ValidateRE(possiblePattern[0].Value, true)); } var validPattern = element.GetElementsByTagName(NATIONAL_NUMBER_PATTERN); if (validPattern.Length > 0) { numberDesc.SetNationalNumberPattern(ValidateRE(validPattern[0].Value, true)); } if (!liteBuild) { var exampleNumber = element.GetElementsByTagName(EXAMPLE_NUMBER); if (exampleNumber.Length > 0) { numberDesc.SetExampleNumber(exampleNumber[0].Value); } } } return(numberDesc.Build()); }
/** * Processes a phone number description element from the XML file and returns it as a * PhoneNumberDesc. If the description element is a fixed line or mobile number, the parent * description will be used to fill in the whole element if necessary, or any components that are * missing. For all other types, the parent description will only be used to fill in missing * components if the type has a partial definition. For example, if no "tollFree" element exists, * we assume there are no toll free numbers for that locale, and return a phone number description * with no national number data and [-1] for the possible lengths. Note that the parent * description must therefore already be processed before this method is called on any child * elements. * * @param generalDesc a generic phone number description that will be used to fill in missing * parts of the description * @param countryElement the XML element representing all the country information * @param numberType the name of the number type, corresponding to the appropriate tag in the XML * file with information about that type * @return complete description of that phone number type */ public static PhoneNumberDesc.Builder ProcessPhoneNumberDescElement(PhoneNumberDesc parentDesc, XElement countryElement, string numberType) { var phoneNumberDescList = countryElement.Elements(numberType).ToList(); var numberDesc = new PhoneNumberDesc.Builder(); if (phoneNumberDescList.Count == 0) { // -1 will never match a possible phone number length, so is safe to use to ensure this never // matches. We don't leave it empty, since for compression reasons, we use the empty list to // mean that the generalDesc possible lengths apply. numberDesc.AddPossibleLength(-1); return(numberDesc); } if (phoneNumberDescList.Count > 1) { throw new Exception($"Multiple elements with type {numberType} found."); } var element = phoneNumberDescList[0]; parentDesc ??= new PhoneNumberDesc(); var lengths = new SortedSet <int>(); var localOnlyLengths = new SortedSet <int>(); PopulatePossibleLengthSets(element.Elements(POSSIBLE_LENGTHS), lengths, localOnlyLengths); SetPossibleLengths(lengths, localOnlyLengths, parentDesc, numberDesc); var validPattern = element.Element(NATIONAL_NUMBER_PATTERN); if (validPattern != null) { numberDesc.SetNationalNumberPattern(ValidateRE(validPattern.Value, true)); } var exampleNumber = element.Element(EXAMPLE_NUMBER); if (exampleNumber != null) { numberDesc.SetExampleNumber(exampleNumber.Value); } return(numberDesc); }
private PhoneNumberDesc GetFiltered(string type, PhoneNumberDesc desc) { var builder = new PhoneNumberDesc.Builder().MergeFrom(desc); if (ShouldDrop(type, "nationalNumberPattern")) { builder.ClearNationalNumberPattern(); } if (ShouldDrop(type, "possibleLength")) { builder.ClearPossibleLength(); } if (ShouldDrop(type, "possibleLengthLocalOnly")) { builder.ClearPossibleLengthLocalOnly(); } if (ShouldDrop(type, "exampleNumber")) { builder.ClearExampleNumber(); } return(builder.Build()); }
/** * Tests whether a short number matches a valid pattern in a region. Note that this doesn't verify * the number is actually in use, which is impossible to tell by just looking at the number * itself. * * @param number the short number for which we want to test the validity * @param regionDialingFrom the region from which the number is dialed * @return whether the short number matches a valid pattern */ public bool isValidShortNumberForRegion(PhoneNumber number, string regionDialingFrom) { if (!RegionDialingFromMatchesNumber(number, regionDialingFrom)) { return(false); } PhoneMetadata phoneMetadata = MetadataManager.GetShortNumberMetadataForRegion(regionDialingFrom); if (phoneMetadata == null) { return(false); } string shortNumber = GetNationalSignificantNumber(number); PhoneNumberDesc generalDesc = phoneMetadata.GeneralDesc; if (!MatchesPossibleNumberAndNationalNumber(shortNumber, generalDesc)) { return(false); } PhoneNumberDesc shortNumberDesc = phoneMetadata.ShortCode; return(MatchesPossibleNumberAndNationalNumber(shortNumber, shortNumberDesc)); }
public Builder MergeFrom(PhoneNumberDesc other) { if (other == global::PhoneNumbers.PhoneNumberDesc.DefaultInstance) return this; if (other.HasNationalNumberPattern) { NationalNumberPattern = other.NationalNumberPattern; } if (other.HasPossibleNumberPattern) { PossibleNumberPattern = other.PossibleNumberPattern; } if (other.possibleLength_.Count != 0) { result.possibleLength_.AddRange(other.possibleLength_); } if (other.possibleLengthLocalOnly_.Count != 0) { result.possibleLengthLocalOnly_.AddRange(other.possibleLengthLocalOnly_); } if (other.HasExampleNumber) { ExampleNumber = other.ExampleNumber; } return this; }
public Builder Clear() { result = new PhoneNumberDesc(); return this; }
public PhoneNumberDesc BuildPartial() { if (result == null) { throw new global::System.InvalidOperationException("build() has already been called on this Builder"); } PhoneNumberDesc returnMe = result; result = null; return returnMe; }
public static Builder CreateBuilder(PhoneNumberDesc prototype) { return (Builder) new Builder().MergeFrom(prototype); }
public static Builder CreateBuilder(PhoneNumberDesc prototype) { return(new Builder().MergeFrom(prototype)); }
/** * Processes a phone number description element from the XML file and returns it as a * PhoneNumberDesc. If the description element is a fixed line or mobile number, the general * description will be used to fill in the whole element if necessary, or any components that are * missing. For all other types, the general description will only be used to fill in missing * components if the type has a partial definition. For example, if no "tollFree" element exists, * we assume there are no toll free numbers for that locale, and return a phone number description * with "NA" for both the national and possible number patterns. * * @param generalDesc a generic phone number description that will be used to fill in missing * parts of the description * @param countryElement the XML element representing all the country information * @param numberType the name of the number type, corresponding to the appropriate tag in the XML * file with information about that type * @return complete description of that phone number type */ public static PhoneNumberDesc ProcessPhoneNumberDescElement(PhoneNumberDesc generalDesc, XmlElement countryElement, String numberType, bool liteBuild) { if (generalDesc == null) generalDesc = new PhoneNumberDesc.Builder().Build(); var phoneNumberDescList = countryElement.GetElementsByTagName(numberType); var numberDesc = new PhoneNumberDesc.Builder(); if (phoneNumberDescList.Count == 0 && !IsValidNumberType(numberType)) { numberDesc.SetNationalNumberPattern("NA"); numberDesc.SetPossibleNumberPattern("NA"); return numberDesc.Build(); } numberDesc.MergeFrom(generalDesc); if (phoneNumberDescList.Count > 0) { XmlElement element = (XmlElement)phoneNumberDescList[0]; var possiblePattern = element.GetElementsByTagName(POSSIBLE_NUMBER_PATTERN); if (possiblePattern.Count > 0) numberDesc.SetPossibleNumberPattern(ValidateRE(possiblePattern[0].InnerText, true)); var validPattern = element.GetElementsByTagName(NATIONAL_NUMBER_PATTERN); if (validPattern.Count > 0) numberDesc.SetNationalNumberPattern(ValidateRE(validPattern[0].InnerText, true)); if (!liteBuild) { var exampleNumber = element.GetElementsByTagName(EXAMPLE_NUMBER); if (exampleNumber.Count > 0) numberDesc.SetExampleNumber(exampleNumber[0].InnerText); } } return numberDesc.Build(); }
private bool IsNumberMatchingDesc(String nationalNumber, PhoneNumberDesc numberDesc) { var possibleNumberPatternMatch = regexCache.GetPatternForRegex( numberDesc.PossibleNumberPattern).MatchAll(nationalNumber); var nationalNumberPatternMatch = regexCache.GetPatternForRegex( numberDesc.NationalNumberPattern).MatchAll(nationalNumber); return possibleNumberPatternMatch.Success && nationalNumberPatternMatch.Success; }
public Builder MergeFrom(PhoneNumberDesc other) { if (other == global::PhoneNumbers.PhoneNumberDesc.DefaultInstance) return this; if (other.HasNationalNumberPattern) { NationalNumberPattern = other.NationalNumberPattern; } if (other.HasPossibleNumberPattern) { PossibleNumberPattern = other.PossibleNumberPattern; } if (other.HasExampleNumber) { ExampleNumber = other.ExampleNumber; } return this; }