Exemplo n.º 1
0
        /**
        * 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
        }
        /**
         * Sets possible lengths in the general description, derived from certain child elements.
         */
        // @VisibleForTesting
        static void SetPossibleLengthsGeneralDesc(PhoneNumberDesc.Builder generalDesc, string metadataId,
                                                  XElement data, bool isShortNumberMetadata)
        {
            var lengths          = new SortedSet <int>();
            var localOnlyLengths = new SortedSet <int>();
            // The general description node should *always* be present if metadata for other types is
            // present, aside from in some unit tests.
            // (However, for e.g. formatting metadata in PhoneNumberAlternateFormats, no PhoneNumberDesc
            // elements are present).
            var generalDescNodes = data.GetElementsByTagName(GENERAL_DESC).ToList();

            if (generalDescNodes.Any())
            {
                var generalDescNode = generalDescNodes.ElementAt(0);
                PopulatePossibleLengthSets(generalDescNode, lengths, localOnlyLengths);
                if (lengths.Count != 0 || localOnlyLengths.Count != 0)
                {
                    // We shouldn't have anything specified at the "general desc" level: we are going to
                    // calculate this ourselves from child elements.
                    throw new Exception("Found possible lengths specified at general " +
                                        $"desc: this should be derived from child elements. Affected country: {metadataId}");
                }
            }
            if (!isShortNumberMetadata)
            {
                // Make a copy here since we want to remove some nodes, but we don't want to do that on our
                // actual data.
                var allDescData = new XElement(data);
                foreach (var tag in PhoneNumberDescsWithoutMatchingTypes)
                {
                    var nodesToRemove = allDescData.GetElementsByTagName(tag).ToList();
                    if (nodesToRemove.Any())
                    {
                        // We check when we process phone number descriptions that there are only one of each
                        // type, so this is safe to do.
                        nodesToRemove.ElementAt(0).Remove();
                    }
                }
                PopulatePossibleLengthSets(allDescData, lengths, localOnlyLengths);
            }
            else
            {
                // For short number metadata, we want to copy the lengths from the "short code" section only.
                // This is because it's the more detailed validation pattern, it's not a sub-type of short
                // codes. The other lengths will be checked later to see that they are a sub-set of these
                // possible lengths.
                var shortCodeDescList = data.GetElementsByTagName(SHORT_CODE).ToList();
                if (shortCodeDescList.Any())
                {
                    var shortCodeDesc = shortCodeDescList.ElementAt(0);
                    PopulatePossibleLengthSets(shortCodeDesc, lengths, localOnlyLengths);
                }
                if (localOnlyLengths.Count > 0)
                {
                    throw new Exception("Found local-only lengths in short-number metadata");
                }
            }
            SetPossibleLengths(lengths, localOnlyLengths, null, generalDesc);
        }
Exemplo n.º 3
0
        /**
         * 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);
        }
        /**
         * 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());
        }
        /**
         * Sets possible lengths in the general description, derived from certain child elements.
         */
        private static void SetPossibleLengthsGeneralDesc(PhoneNumberDesc.Builder generalDesc, string metadataId,
                                                          XElement data, bool isShortNumberMetadata)
        {
            var lengths          = new SortedSet <int>();
            var localOnlyLengths = new SortedSet <int>();
            // The general description node should *always* be present if metadata for other types is
            // present, aside from in some unit tests.
            // (However, for e.g. formatting metadata in PhoneNumberAlternateFormats, no PhoneNumberDesc
            // elements are present).
            var generalDescNode = data.Element(GENERAL_DESC);

            if (generalDescNode != null)
            {
                PopulatePossibleLengthSets(generalDescNode.Elements(POSSIBLE_LENGTHS), lengths, localOnlyLengths);
                if (lengths.Count != 0 || localOnlyLengths.Count != 0)
                {
                    throw new Exception("Found possible lengths specified at general " +
                                        $"desc: this should be derived from child elements. Affected country: {metadataId}");
                }
            }
            if (!isShortNumberMetadata)
            {
                var allDescData = data.Descendants(POSSIBLE_LENGTHS).Where(e => e.Parent.Name != NO_INTERNATIONAL_DIALLING);
                PopulatePossibleLengthSets(allDescData, lengths, localOnlyLengths);
            }
            else
            {
                // For short number metadata, we want to copy the lengths from the "short code" section only.
                // This is because it's the more detailed validation pattern, it's not a sub-type of short
                // codes. The other lengths will be checked later to see that they are a sub-set of these
                // possible lengths.
                var shortCodeDesc = data.Element(SHORT_CODE);
                if (shortCodeDesc != null)
                {
                    PopulatePossibleLengthSets(shortCodeDesc.Elements(POSSIBLE_LENGTHS), lengths, localOnlyLengths);
                }
                if (localOnlyLengths.Count > 0)
                {
                    throw new Exception("Found local-only lengths in short-number metadata");
                }
            }
            SetPossibleLengths(lengths, localOnlyLengths, null, generalDesc);
        }
        /**
         * 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);
        }
Exemplo n.º 7
0
        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());
        }
        /**
        * 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();
        }
 /**
  * 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)}.");
             }
         }
     }
 }