public static void SetLeadingDigitsPatterns(XElement numberFormatElement, NumberFormat.Builder format)
 {
     foreach (XElement e in numberFormatElement.GetElementsByTagName(LEADING_DIGITS))
     {
         format.AddLeadingDigitsPattern(ValidateRE(e.Value, true));
     }
 }
        /**
         * Extracts the pattern for international format. If there is no intlFormat, default to using the
         * national format. If the intlFormat is set to "NA" the intlFormat should be ignored.
         *
         * @throws  RuntimeException if multiple intlFormats have been encountered.
         * @return  whether an international number format is defined.
         */
        public static bool LoadInternationalFormat(PhoneMetadata.Builder metadata,
                                                   XElement numberFormatElement,
                                                   string nationalFormat)
        {
            var intlFormat = new NumberFormat.Builder();

            SetLeadingDigitsPatterns(numberFormatElement, intlFormat);
            intlFormat.SetPattern(numberFormatElement.GetAttribute(PATTERN));
            var intlFormatPattern            = numberFormatElement.GetElementsByTagName(INTL_FORMAT).ToList();
            var hasExplicitIntlFormatDefined = false;

            if (intlFormatPattern.Count > 1)
            {
                throw new Exception("Invalid number of intlFormat patterns for country: " +
                                    metadata.Id);
            }
            if (intlFormatPattern.Count == 0)
            {
                // Default to use the same as the national pattern if none is defined.
                intlFormat.SetFormat(nationalFormat);
            }
            else
            {
                var intlFormatPatternValue = intlFormatPattern.First().Value;
                intlFormat.SetFormat(intlFormatPatternValue);
                hasExplicitIntlFormatDefined = true;
            }

            if (intlFormat.HasFormat)
            {
                metadata.AddIntlNumberFormat(intlFormat);
            }
            return(hasExplicitIntlFormatDefined);
        }
Exemple #3
0
        /**
        *  Extracts the available formats from the provided DOM element. If it does not contain any
        *  nationalPrefixFormattingRule, the one passed-in is retained. The nationalPrefix,
        *  nationalPrefixFormattingRule and nationalPrefixOptionalWhenFormatting values are provided from
        *  the parent (territory) element.
        */
        public static void LoadAvailableFormats(PhoneMetadata.Builder metadata,
            XElement element, string nationalPrefix,
            string nationalPrefixFormattingRule,
            bool nationalPrefixOptionalWhenFormatting)
        {
            var carrierCodeFormattingRule = "";
            if (element.HasAttribute(CARRIER_CODE_FORMATTING_RULE))
                carrierCodeFormattingRule = ValidateRE(
                    GetDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix));
            var numberFormatElements = element.GetElementsByTagName(NUMBER_FORMAT).ToList();
            var hasExplicitIntlFormatDefined = false;

            var numOfFormatElements = numberFormatElements.Count;
            if (numOfFormatElements > 0)
            {
                foreach (var numberFormatElement in numberFormatElements)
                {
                    var format = new NumberFormat.Builder();

                    if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_FORMATTING_RULE))
                    {
                        format.SetNationalPrefixFormattingRule(
                            GetNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix));
                        format.SetNationalPrefixOptionalWhenFormatting(
                            numberFormatElement.HasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING));
                    }
                    else
                    {
                        format.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule);
                        format.SetNationalPrefixOptionalWhenFormatting(nationalPrefixOptionalWhenFormatting);
                    }
                    if (numberFormatElement.HasAttribute("carrierCodeFormattingRule"))
                        format.SetDomesticCarrierCodeFormattingRule(ValidateRE(
                            GetDomesticCarrierCodeFormattingRuleFromElement(
                                numberFormatElement, nationalPrefix)));
                    else
                        format.SetDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);

                    // Extract the pattern for the national format.
                    var nationalFormat =
                        LoadNationalFormat(metadata, numberFormatElement, format);
                    metadata.AddNumberFormat(format);

                    if (LoadInternationalFormat(metadata, numberFormatElement, nationalFormat))
                        hasExplicitIntlFormatDefined = true;
                }
                // Only a small number of regions need to specify the intlFormats in the xml. For the majority
                // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat
                // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that
                // actually differ in some way to the national formats.
                if (!hasExplicitIntlFormatDefined)
                    metadata.ClearIntlNumberFormat();
            }
        }
Exemple #4
0
        /**
         * Extracts the pattern for the national format.
         *
         * @throws  RuntimeException if multiple or no formats have been encountered.
         * @return  the national format string.
         */
        public static string LoadNationalFormat(PhoneMetadata.Builder metadata, XElement numberFormatElement,
            NumberFormat.Builder format)
        {
            SetLeadingDigitsPatterns(numberFormatElement, format);
            format.SetPattern(ValidateRE(numberFormatElement.GetAttribute(PATTERN)));

            var formatPattern = numberFormatElement.GetElementsByTagName(FORMAT).ToList();
            if (formatPattern.Count != 1)
                throw new Exception("Invalid number of format patterns for country: " +
                                    metadata.Id);
            var nationalFormat = formatPattern[0].Value;
            format.SetFormat(nationalFormat);
            return nationalFormat;
        }
        /**
         * Extracts the pattern for the national format.
         *
         * @throws  RuntimeException if multiple or no formats have been encountered.
         * @return  the national format string.
         */
        // @VisibleForTesting
        public static String LoadNationalFormat(PhoneMetadata.Builder metadata, XElement numberFormatElement,
                                                NumberFormat.Builder format)
        {
            SetLeadingDigitsPatterns(numberFormatElement, format);
            format.SetPattern(ValidateRE(numberFormatElement.GetAttribute(PATTERN)));

            var formatPattern = numberFormatElement.Elements(FORMAT).ToArray();

            if (formatPattern.Count() != 1)
            {
                //LOGGER.log(Level.SEVERE,
                //           "Only one format pattern for a numberFormat element should be defined.");
                throw new Exception("Invalid number of format patterns for country: " +
                                    metadata.Id);
            }
            String nationalFormat = formatPattern[0].Value;

            format.SetFormat(nationalFormat);
            return(nationalFormat);
        }
        /**
         * Extracts the pattern for international format. If there is no intlFormat, default to using the
         * national format. If the intlFormat is set to "NA" the intlFormat should be ignored.
         *
         * @throws  RuntimeException if multiple intlFormats have been encountered.
         * @return  whether an international number format is defined.
         */
        // @VisibleForTesting
        public static bool LoadInternationalFormat(PhoneMetadata.Builder metadata,
                                                   XElement numberFormatElement,
                                                   String nationalFormat)
        {
            var intlFormat = new NumberFormat.Builder();

            SetLeadingDigitsPatterns(numberFormatElement, intlFormat);
            intlFormat.SetPattern(numberFormatElement.GetAttribute(PATTERN));
            var  intlFormatPattern            = numberFormatElement.GetElementsByTagName(INTL_FORMAT);
            bool hasExplicitIntlFormatDefined = false;

            if (intlFormatPattern.Length > 1)
            {
                //LOGGER.log(Level.SEVERE,
                //          "A maximum of one intlFormat pattern for a numberFormat element should be " +
                //           "defined.");
                throw new Exception("Invalid number of intlFormat patterns for country: " +
                                    metadata.Id);
            }
            if (intlFormatPattern.Length == 0)
            {
                // Default to use the same as the national pattern if none is defined.
                intlFormat.SetFormat(nationalFormat);
            }
            else
            {
                String intlFormatPatternValue = intlFormatPattern[0].Value;
                if (!intlFormatPatternValue.Equals("NA"))
                {
                    intlFormat.SetFormat(intlFormatPatternValue);
                }
                hasExplicitIntlFormatDefined = true;
            }

            if (intlFormat.HasFormat)
            {
                metadata.AddIntlNumberFormat(intlFormat);
            }
            return(hasExplicitIntlFormatDefined);
        }
        /**
        * Extracts the pattern for international format. If there is no intlFormat, default to using the
        * national format. If the intlFormat is set to "NA" the intlFormat should be ignored.
        *
        * @throws  RuntimeException if multiple intlFormats have been encountered.
        * @return  whether an international number format is defined.
        */
        // @VisibleForTesting
        public static bool LoadInternationalFormat(PhoneMetadata.Builder metadata,
            XmlElement numberFormatElement,
            String nationalFormat)
        {
            NumberFormat.Builder intlFormat = new NumberFormat.Builder();
            SetLeadingDigitsPatterns(numberFormatElement, intlFormat);
            intlFormat.SetPattern(numberFormatElement.GetAttribute(PATTERN));
            var intlFormatPattern = numberFormatElement.GetElementsByTagName(INTL_FORMAT);
            bool hasExplicitIntlFormatDefined = false;

            if (intlFormatPattern.Count > 1)
            {
                //LOGGER.log(Level.SEVERE,
                //          "A maximum of one intlFormat pattern for a numberFormat element should be " +
                //           "defined.");
                throw new Exception("Invalid number of intlFormat patterns for country: " +
                                    metadata.Id);
            }
            else if (intlFormatPattern.Count == 0)
            {
                // Default to use the same as the national pattern if none is defined.
                intlFormat.SetFormat(nationalFormat);
            }
            else
            {
                String intlFormatPatternValue = intlFormatPattern[0].InnerText;
                if (!intlFormatPatternValue.Equals("NA"))
                {
                    intlFormat.SetFormat(intlFormatPatternValue);
                }
                hasExplicitIntlFormatDefined = true;
            }

            if (intlFormat.HasFormat)
            {
                metadata.AddIntlNumberFormat(intlFormat);
            }
            return hasExplicitIntlFormatDefined;
        }
        /**
        *  Extracts the available formats from the provided DOM element. If it does not contain any
        *  nationalPrefixFormattingRule, the one passed-in is retained. The nationalPrefix,
        *  nationalPrefixFormattingRule and nationalPrefixOptionalWhenFormatting values are provided from
        *  the parent (territory) element.
        */
        // @VisibleForTesting
        public static void LoadAvailableFormats(PhoneMetadata.Builder metadata,
                                         XmlElement element, String nationalPrefix,
                                         String nationalPrefixFormattingRule,
                                         bool nationalPrefixOptionalWhenFormatting)
        {
            String carrierCodeFormattingRule = "";
            if (element.HasAttribute(CARRIER_CODE_FORMATTING_RULE))
            {
                carrierCodeFormattingRule = ValidateRE(
                    GetDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix));
            }
            var numberFormatElements = element.GetElementsByTagName(NUMBER_FORMAT);
            bool hasExplicitIntlFormatDefined = false;

            int numOfFormatElements = numberFormatElements.Count;
            if (numOfFormatElements > 0)
            {
                foreach (XmlElement numberFormatElement in numberFormatElements)
                {
                    var format = new NumberFormat.Builder();

                    if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_FORMATTING_RULE))
                    {
                        format.SetNationalPrefixFormattingRule(
                            GetNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix));
                        format.SetNationalPrefixOptionalWhenFormatting(
                            numberFormatElement.HasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING));

                    }
                    else
                    {
                        format.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule);
                        format.SetNationalPrefixOptionalWhenFormatting(nationalPrefixOptionalWhenFormatting);
                    }
                    if (numberFormatElement.HasAttribute("carrierCodeFormattingRule"))
                    {
                        format.SetDomesticCarrierCodeFormattingRule(ValidateRE(
                            GetDomesticCarrierCodeFormattingRuleFromElement(
                                numberFormatElement, nationalPrefix)));
                    }
                    else
                    {
                        format.SetDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
                    }

                    // Extract the pattern for the national format.
                    String nationalFormat =
                        LoadNationalFormat(metadata, numberFormatElement, format);
                    metadata.AddNumberFormat(format);

                    if (LoadInternationalFormat(metadata, numberFormatElement, nationalFormat))
                    {
                        hasExplicitIntlFormatDefined = true;
                    }
                }
                // Only a small number of regions need to specify the intlFormats in the xml. For the majority
                // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat
                // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that
                // actually differ in some way to the national formats.
                if (!hasExplicitIntlFormatDefined)
                {
                    metadata.ClearIntlNumberFormat();
                }
            }
        }
        /**
        * Formats a phone number using the original phone number format that the number is parsed from.
        * The original format is embedded in the country_code_source field of the PhoneNumber object
        * passed in. If such information is missing, the number will be formatted into the NATIONAL
        * format by default. When the number contains a leading zero and this is unexpected for this
        * country, or we don't have a formatting pattern for the number, the method returns the raw input
        * when it is available.
        *
        * Note this method guarantees no digit will be inserted, removed or modified as a result of
        * formatting.
        *
        * @param number  the phone number that needs to be formatted in its original number format
        * @param regionCallingFrom  the region whose IDD needs to be prefixed if the original number
        *     has one
        * @return  the formatted phone number in its original number format
        */
        public String FormatInOriginalFormat(PhoneNumber number, String regionCallingFrom)
        {
            if (number.HasRawInput &&
                (HasUnexpectedItalianLeadingZero(number) || !HasFormattingPatternForNumber(number)))
            {
                // We check if we have the formatting pattern because without that, we might format the number
                // as a group without national prefix.
                return number.RawInput;
            }

            if (!number.HasCountryCodeSource)
                return Format(number, PhoneNumberFormat.NATIONAL);

            String formattedNumber;
            switch (number.CountryCodeSource)
            {
                case CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN:
                    formattedNumber = Format(number, PhoneNumberFormat.INTERNATIONAL);
                    break;
                case CountryCodeSource.FROM_NUMBER_WITH_IDD:
                    formattedNumber = FormatOutOfCountryCallingNumber(number, regionCallingFrom);
                    break;
                case CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN:
                    formattedNumber = Format(number, PhoneNumberFormat.INTERNATIONAL).Substring(1);
                    break;
                case CountryCodeSource.FROM_DEFAULT_COUNTRY:
                    // Fall-through to default case.
                default:
                    String regionCode = GetRegionCodeForCountryCode(number.CountryCode);
                    // We strip non-digits from the NDD here, and from the raw input later, so that we can
                    // compare them easily.
                    String nationalPrefix = GetNddPrefixForRegion(regionCode, true /* strip non-digits */);
                    String nationalFormat = Format(number, PhoneNumberFormat.NATIONAL);
                    if (nationalPrefix == null || nationalPrefix.Length == 0)
                    {
                        // If the region doesn't have a national prefix at all, we can safely return the national
                        // format without worrying about a national prefix being added.
                        formattedNumber = nationalFormat;
                        break;
                    }
                    // Otherwise, we check if the original number was entered with a national prefix.
                    if (RawInputContainsNationalPrefix(
                        number.RawInput, nationalPrefix, regionCode))
                    {
                        // If so, we can safely return the national format.
                        formattedNumber = nationalFormat;
                        break;
                    }
                    PhoneMetadata metadata = GetMetadataForRegion(regionCode);
                    String nationalNumber = GetNationalSignificantNumber(number);
                    NumberFormat formatRule =
                        ChooseFormattingPatternForNumber(metadata.NumberFormatList, nationalNumber);
                    // When the format we apply to this number doesn't contain national prefix, we can just
                    // return the national format.
                    // TODO: Refactor the code below with the code in isNationalPrefixPresentIfRequired.
                    String candidateNationalPrefixRule = formatRule.NationalPrefixFormattingRule;
                    // We assume that the first-group symbol will never be _before_ the national prefix.
                    int indexOfFirstGroup = candidateNationalPrefixRule.IndexOf("${1}");
                    if (indexOfFirstGroup <= 0)
                    {
                        formattedNumber = nationalFormat;
                        break;
                    }
                    candidateNationalPrefixRule =
                        candidateNationalPrefixRule.Substring(0, indexOfFirstGroup);
                    candidateNationalPrefixRule = NormalizeDigitsOnly(candidateNationalPrefixRule);
                    if (candidateNationalPrefixRule.Length == 0)
                    {
                        // National prefix not used when formatting this number.
                        formattedNumber = nationalFormat;
                        break;
                    }
                    // Otherwise, we need to remove the national prefix from our output.
                    var numFormatCopy = new NumberFormat.Builder()
                        .MergeFrom(formatRule)
                        .ClearNationalPrefixFormattingRule()
                        .Build();
                    List<NumberFormat> numberFormats = new List<NumberFormat>(1);
                    numberFormats.Add(numFormatCopy);
                    formattedNumber = FormatByPattern(number, PhoneNumberFormat.NATIONAL, numberFormats);
                    break;
            }
            String rawInput = number.RawInput;
            // If no digit is inserted/removed/modified as a result of our formatting, we return the
            // formatted phone number; otherwise we return the raw input the user entered.
            return (formattedNumber != null &&
                NormalizeHelper(formattedNumber, DIALLABLE_CHAR_MAPPINGS, true /* remove non matches */)
                    .Equals(NormalizeHelper(
                        rawInput, DIALLABLE_CHAR_MAPPINGS, true /* remove non matches */)))
                ? formattedNumber
                : rawInput;
        }
        /**
        * Formats a phone number in the specified format using client-defined formatting rules. Note that
        * if the phone number has a country calling code of zero or an otherwise invalid country calling
        * code, we cannot work out things like whether there should be a national prefix applied, or how
        * to format extensions, so we return the national significant number with no formatting applied.
        *
        * @param number                        the phone number to be formatted
        * @param numberFormat                  the format the phone number should be formatted into
        * @param userDefinedFormats            formatting rules specified by clients
        * @return  the formatted phone number
        */
        public String FormatByPattern(PhoneNumber number, PhoneNumberFormat numberFormat,
            List<NumberFormat> userDefinedFormats)
        {
            int countryCallingCode = number.CountryCode;
            var nationalSignificantNumber = GetNationalSignificantNumber(number);
            // Note getRegionCodeForCountryCode() is used because formatting information for regions which
            // share a country calling code is contained by only one region for performance reasons. For
            // example, for NANPA regions it will be contained in the metadata for US.
            var regionCode = GetRegionCodeForCountryCode(countryCallingCode);
            if (!HasValidCountryCallingCode(countryCallingCode))
                return nationalSignificantNumber;

            PhoneMetadata metadata = GetMetadataForRegionOrCallingCode(countryCallingCode, regionCode);
            StringBuilder formattedNumber = new StringBuilder(20);
            NumberFormat formattingPattern =
                ChooseFormattingPatternForNumber(userDefinedFormats, nationalSignificantNumber);
            if (formattingPattern == null)
            {
                // If no pattern above is matched, we format the number as a whole.
                formattedNumber.Append(nationalSignificantNumber);
            }
            else
            {
                var numFormatCopy = new NumberFormat.Builder();
                // Before we do a replacement of the national prefix pattern $NP with the national prefix, we
                // need to copy the rule so that subsequent replacements for different numbers have the
                // appropriate national prefix.
                numFormatCopy.MergeFrom(formattingPattern);
                String nationalPrefixFormattingRule = formattingPattern.NationalPrefixFormattingRule;
                if (nationalPrefixFormattingRule.Length > 0)
                {
                    String nationalPrefix = metadata.NationalPrefix;
                    if (nationalPrefix.Length > 0)
                    {
                        // Replace $NP with national prefix and $FG with the first group ($1).
                        nationalPrefixFormattingRule = NP_PATTERN.Replace(nationalPrefixFormattingRule, nationalPrefix, 1);
                        nationalPrefixFormattingRule = FG_PATTERN.Replace(nationalPrefixFormattingRule, "$$1", 1);
                        numFormatCopy.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule);
                    }
                    else
                    {
                        // We don't want to have a rule for how to format the national prefix if there isn't one.
                        numFormatCopy.ClearNationalPrefixFormattingRule();
                    }
                }
                formattedNumber.Append(
                    FormatNsnUsingPattern(nationalSignificantNumber, numFormatCopy.Build(), numberFormat));
            }
            MaybeAppendFormattedExtension(number, metadata, numberFormat, formattedNumber);
            PrefixNumberWithCountryCallingCode(countryCallingCode, numberFormat, formattedNumber);
            return formattedNumber.ToString();
        }
        /**
        * Formats a phone number for out-of-country dialing purposes.
        *
        * Note that in this version, if the number was entered originally using alpha characters and
        * this version of the number is stored in raw_input, this representation of the number will be
        * used rather than the digit representation. Grouping information, as specified by characters
        * such as "-" and " ", will be retained.
        *
        * <p><b>Caveats:</b></p>
        * <ul>
        *  <li> This will not produce good results if the country calling code is both present in the raw
        *       input _and_ is the start of the national number. This is not a problem in the regions
        *       which typically use alpha numbers.
        *  <li> This will also not produce good results if the raw input has any grouping information
        *       within the first three digits of the national number, and if the function needs to strip
        *       preceding digits/words in the raw input before these digits. Normally people group the
        *       first three digits together so this is not a huge problem - and will be fixed if it
        *       proves to be so.
        * </ul>
        *
        * @param number  the phone number that needs to be formatted
        * @param regionCallingFrom  the region where the call is being placed
        * @return  the formatted phone number
        */
        public String FormatOutOfCountryKeepingAlphaChars(PhoneNumber number, String regionCallingFrom)
        {
            var rawInput = number.RawInput;
            // If there is no raw input, then we can't keep alpha characters because there aren't any.
            // In this case, we return formatOutOfCountryCallingNumber.
            if (rawInput.Length == 0)
                return FormatOutOfCountryCallingNumber(number, regionCallingFrom);

            int countryCode = number.CountryCode;
            if (!HasValidCountryCallingCode(countryCode))
                return rawInput;

            // Strip any prefix such as country calling code, IDD, that was present. We do this by comparing
            // the number in raw_input with the parsed number.
            // To do this, first we normalize punctuation. We retain number grouping symbols such as " "
            // only.
            rawInput = NormalizeHelper(rawInput, ALL_PLUS_NUMBER_GROUPING_SYMBOLS, true);
            // Now we trim everything before the first three digits in the parsed number. We choose three
            // because all valid alpha numbers have 3 digits at the start - if it does not, then we don't
            // trim anything at all. Similarly, if the national number was less than three digits, we don't
            // trim anything at all.
            var nationalNumber = GetNationalSignificantNumber(number);
            if (nationalNumber.Length > 3)
            {
                int firstNationalNumberDigit = rawInput.IndexOf(nationalNumber.Substring(0, 3));
                if (firstNationalNumberDigit != -1)
                    rawInput = rawInput.Substring(firstNationalNumberDigit);
            }
            var metadataForRegionCallingFrom = GetMetadataForRegion(regionCallingFrom);
            if (countryCode == NANPA_COUNTRY_CODE)
            {
                if (IsNANPACountry(regionCallingFrom))
                    return countryCode + " " + rawInput;
            }
            else if (IsValidRegionCode(regionCallingFrom) &&
                countryCode == GetCountryCodeForValidRegion(regionCallingFrom))
            {
                NumberFormat formattingPattern =
                    ChooseFormattingPatternForNumber(metadataForRegionCallingFrom.NumberFormatList,
                        nationalNumber);
                if (formattingPattern == null)
                    // If no pattern above is matched, we format the original input.
                    return rawInput;

                var newFormat = new NumberFormat.Builder();
                newFormat.MergeFrom(formattingPattern);
                // The first group is the first group of digits that the user wrote together.
                newFormat.SetPattern("(\\d+)(.*)");
                // Here we just concatenate them back together after the national prefix has been fixed.
                newFormat.SetFormat("$1$2");
                // Now we format using this pattern instead of the default pattern, but with the national
                // prefix prefixed if necessary.
                // This will not work in the cases where the pattern (and not the leading digits) decide
                // whether a national prefix needs to be used, since we have overridden the pattern to match
                // anything, but that is not the case in the metadata to date.
                return FormatNsnUsingPattern(rawInput, newFormat.Build(), PhoneNumberFormat.NATIONAL);
            }
            String internationalPrefixForFormatting = "";
            // If an unsupported region-calling-from is entered, or a country with multiple international
            // prefixes, the international format of the number is returned, unless there is a preferred
            // international prefix.
            if (metadataForRegionCallingFrom != null)
            {
                String internationalPrefix = metadataForRegionCallingFrom.InternationalPrefix;
                internationalPrefixForFormatting =
                    UNIQUE_INTERNATIONAL_PREFIX.MatchAll(internationalPrefix).Success
                        ? internationalPrefix
                        : metadataForRegionCallingFrom.PreferredInternationalPrefix;
            }
            var formattedNumber = new StringBuilder(rawInput);
            String regionCode = GetRegionCodeForCountryCode(countryCode);
            PhoneMetadata metadataForRegion = GetMetadataForRegionOrCallingCode(countryCode, regionCode);
            MaybeAppendFormattedExtension(number, metadataForRegion,
                PhoneNumberFormat.INTERNATIONAL, formattedNumber);
            if (internationalPrefixForFormatting.Length > 0)
            {
                formattedNumber.Insert(0, " ").Insert(0, countryCode).Insert(0, " ")
                    .Insert(0, internationalPrefixForFormatting);
            }
            else
            {
                // Invalid region entered as country-calling-from (so no metadata was found for it) or the
                // region chosen has multiple international dialling prefixes.
                // LOGGER.log(Level.WARNING,
                // "Trying to format number from invalid region "
                // + regionCallingFrom
                // + ". International formatting applied.");
                PrefixNumberWithCountryCallingCode(countryCode, PhoneNumberFormat.INTERNATIONAL,
                    formattedNumber);
            }
            return formattedNumber.ToString();
        }
        /**
        * Formats a phone number in the specified format using client-defined formatting rules. Note that
        * if the phone number has a country calling code of zero or an otherwise invalid country calling
        * code, we cannot work out things like whether there should be a national prefix applied, or how
        * to format extensions, so we return the national significant number with no formatting applied.
        *
        * @param number                        the phone number to be formatted
        * @param numberFormat                  the format the phone number should be formatted into
        * @param userDefinedFormats            formatting rules specified by clients
        * @return  the formatted phone number
        */
        public String FormatByPattern(PhoneNumber number, PhoneNumberFormat numberFormat,
            List<NumberFormat> userDefinedFormats)
        {
            int countryCallingCode = number.CountryCode;
            var nationalSignificantNumber = GetNationalSignificantNumber(number);
            // Note getRegionCodeForCountryCode() is used because formatting information for regions which
            // share a country calling code is contained by only one region for performance reasons. For
            // example, for NANPA regions it will be contained in the metadata for US.
            var regionCode = GetRegionCodeForCountryCode(countryCallingCode);
            if (!HasValidRegionCode(regionCode, countryCallingCode, nationalSignificantNumber))
                return nationalSignificantNumber;

            var userDefinedFormatsCopy = new List<NumberFormat>(userDefinedFormats.Count);
            foreach (var numFormat in userDefinedFormats)
            {
                var nationalPrefixFormattingRule = numFormat.NationalPrefixFormattingRule;
                if (nationalPrefixFormattingRule.Length > 0)
                {
                    // Before we do a replacement of the national prefix pattern $NP with the national prefix,
                    // we need to copy the rule so that subsequent replacements for different numbers have the
                    // appropriate national prefix.
                    var numFormatCopy = new NumberFormat.Builder();
                    numFormatCopy.MergeFrom(numFormat);
                    var nationalPrefix = GetMetadataForRegion(regionCode).NationalPrefix;
                    if (nationalPrefix.Length > 0)
                    {
                        // Replace $NP with national prefix and $FG with the first group ($1).
                        nationalPrefixFormattingRule = NP_PATTERN.Replace(nationalPrefixFormattingRule, nationalPrefix, 1);
                        nationalPrefixFormattingRule = FG_PATTERN.Replace(nationalPrefixFormattingRule, "$$1", 1);
                        numFormatCopy.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule);
                    }
                    else
                    {
                        // We don't want to have a rule for how to format the national prefix if there isn't one.
                        numFormatCopy.ClearNationalPrefixFormattingRule();
                    }
                    userDefinedFormatsCopy.Add(numFormatCopy.Build());
                }
                else
                {
                    // Otherwise, we just add the original rule to the modified list of formats.
                    userDefinedFormatsCopy.Add(numFormat);
                }
            }

            var formattedNumber = new StringBuilder(FormatAccordingToFormats(nationalSignificantNumber,
                userDefinedFormatsCopy, numberFormat));
            MaybeGetFormattedExtension(number, regionCode, numberFormat, formattedNumber);
            FormatNumberByFormat(countryCallingCode, numberFormat, formattedNumber);
            return formattedNumber.ToString();
        }
        /**
        * Formats a phone number for out-of-country dialing purposes.
        *
        * Note that in this version, if the number was entered originally using alpha characters and
        * this version of the number is stored in raw_input, this representation of the number will be
        * used rather than the digit representation. Grouping information, as specified by characters
        * such as "-" and " ", will be retained.
        *
        * <p><b>Caveats:</b></p>
        * <ul>
        *  <li> This will not produce good results if the country calling code is both present in the raw
        *       input _and_ is the start of the national number. This is not a problem in the regions
        *       which typically use alpha numbers.
        *  <li> This will also not produce good results if the raw input has any grouping information
        *       within the first three digits of the national number, and if the function needs to strip
        *       preceding digits/words in the raw input before these digits. Normally people group the
        *       first three digits together so this is not a huge problem - and will be fixed if it
        *       proves to be so.
        * </ul>
        *
        * @param number  the phone number that needs to be formatted
        * @param regionCallingFrom  the region where the call is being placed
        * @return  the formatted phone number
        */
        public String FormatOutOfCountryKeepingAlphaChars(PhoneNumber number, String regionCallingFrom)
        {
            var rawInput = number.RawInput;
            // If there is no raw input, then we can't keep alpha characters because there aren't any.
            // In this case, we return formatOutOfCountryCallingNumber.
            if (rawInput.Length == 0)
                return FormatOutOfCountryCallingNumber(number, regionCallingFrom);

            int countryCode = number.CountryCode;
            var regionCode = GetRegionCodeForCountryCode(countryCode);
            if (!HasValidRegionCode(regionCode, countryCode, rawInput))
                return rawInput;

            // Strip any prefix such as country calling code, IDD, that was present. We do this by comparing
            // the number in raw_input with the parsed number.
            // To do this, first we normalize punctuation. We retain number grouping symbols such as " "
            // only.
            rawInput = NormalizeHelper(rawInput, ALL_PLUS_NUMBER_GROUPING_SYMBOLS, true);
            // Now we trim everything before the first three digits in the parsed number. We choose three
            // because all valid alpha numbers have 3 digits at the start - if it does not, then we don't
            // trim anything at all. Similarly, if the national number was less than three digits, we don't
            // trim anything at all.
            var nationalNumber = GetNationalSignificantNumber(number);
            if (nationalNumber.Length > 3)
            {
                int firstNationalNumberDigit = rawInput.IndexOf(nationalNumber.Substring(0, 3));
                if (firstNationalNumberDigit != -1)
                    rawInput = rawInput.Substring(firstNationalNumberDigit);
            }
            var metadata = GetMetadataForRegion(regionCallingFrom);
            if (countryCode == NANPA_COUNTRY_CODE)
            {
                if (IsNANPACountry(regionCallingFrom))
                    return countryCode + " " + rawInput;
            }
            else if (countryCode == GetCountryCodeForRegion(regionCallingFrom))
            {
                // Here we copy the formatting rules so we can modify the pattern we expect to match against.
                var availableFormats = new List<NumberFormat>(metadata.NumberFormatCount);
                foreach (var format in metadata.NumberFormatList)
                {
                    var newFormat = new NumberFormat.Builder();
                    newFormat.MergeFrom(format);
                    // The first group is the first group of digits that the user determined.
                    newFormat.SetPattern("(\\d+)(.*)");
                    // Here we just concatenate them back together after the national prefix has been fixed.
                    newFormat.SetFormat("$1$2");
                    availableFormats.Add(newFormat.Build());
                }
                // Now we format using these patterns instead of the default pattern, but with the national
                // prefix prefixed if necessary, by choosing the format rule based on the leading digits
                // present in the unformatted national number.
                // This will not work in the cases where the pattern (and not the leading digits) decide
                // whether a national prefix needs to be used, since we have overridden the pattern to match
                // anything, but that is not the case in the metadata to date.
                return FormatAccordingToFormats(rawInput, availableFormats, PhoneNumberFormat.NATIONAL);
            }
            var internationalPrefix = metadata.InternationalPrefix;
            // For countries that have multiple international prefixes, the international format of the
            // number is returned, unless there is a preferred international prefix.
            String internationalPrefixForFormatting =
                UNIQUE_INTERNATIONAL_PREFIX.MatchAll(internationalPrefix).Success
                    ? internationalPrefix
                    : metadata.PreferredInternationalPrefix;
            var formattedNumber = new StringBuilder(rawInput);
            MaybeGetFormattedExtension(number, regionCode, PhoneNumberFormat.INTERNATIONAL,
                formattedNumber);
            if (internationalPrefixForFormatting.Length > 0)
            {
                formattedNumber.Insert(0, " ").Insert(0, countryCode.ToString(CultureInfo.InvariantCulture)).Insert(0, " ")
                    .Insert(0, internationalPrefixForFormatting);
            }
            else
            {
                FormatNumberByFormat(countryCode, PhoneNumberFormat.INTERNATIONAL,
                    formattedNumber);
            }
            return formattedNumber.ToString();
        }
        /**
         *  Extracts the available formats from the provided DOM element. If it does not contain any
         *  nationalPrefixFormattingRule, the one passed-in is retained. The nationalPrefix,
         *  nationalPrefixFormattingRule and nationalPrefixOptionalWhenFormatting values are provided from
         *  the parent (territory) element.
         */
        public static void LoadAvailableFormats(PhoneMetadata.Builder metadata,
                                                XElement element, string nationalPrefix,
                                                string nationalPrefixFormattingRule,
                                                bool nationalPrefixOptionalWhenFormatting)
        {
            var carrierCodeFormattingRule = "";

            if (element.HasAttribute(CARRIER_CODE_FORMATTING_RULE))
            {
                carrierCodeFormattingRule = ValidateRE(
                    GetDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix));
            }

            var availableFormats             = element.Element("availableFormats");
            var hasExplicitIntlFormatDefined = false;

            if (availableFormats != null && availableFormats.HasElements)
            {
                foreach (var numberFormatElement in availableFormats.Elements())
                {
                    var format = new NumberFormat.Builder();

                    if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_FORMATTING_RULE))
                    {
                        format.SetNationalPrefixFormattingRule(
                            GetNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix));
                    }
                    else if (!nationalPrefixFormattingRule.Equals(""))
                    {
                        format.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule);
                    }

                    if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING))
                    {
                        format.SetNationalPrefixOptionalWhenFormatting(
                            bool.Parse(numberFormatElement.Attribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING).Value));
                    }
                    else if (format.NationalPrefixOptionalWhenFormatting
                             != nationalPrefixOptionalWhenFormatting)
                    {
                        // Inherit from the parent field if it is not already the same as the default.
                        format.SetNationalPrefixOptionalWhenFormatting(nationalPrefixOptionalWhenFormatting);
                    }
                    if (numberFormatElement.HasAttribute("carrierCodeFormattingRule"))
                    {
                        format.SetDomesticCarrierCodeFormattingRule(ValidateRE(
                                                                        GetDomesticCarrierCodeFormattingRuleFromElement(
                                                                            numberFormatElement, nationalPrefix)));
                    }
                    else if (!carrierCodeFormattingRule.Equals(""))
                    {
                        format.SetDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
                    }

                    // Extract the pattern for the national format.
                    var nationalFormat =
                        LoadNationalFormat(metadata, numberFormatElement, format);
                    metadata.AddNumberFormat(format);

                    if (LoadInternationalFormat(metadata, numberFormatElement, nationalFormat))
                    {
                        hasExplicitIntlFormatDefined = true;
                    }
                }
                // Only a small number of regions need to specify the intlFormats in the xml. For the majority
                // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat
                // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that
                // actually differ in some way to the national formats.
                if (!hasExplicitIntlFormatDefined)
                {
                    metadata.ClearIntlNumberFormat();
                }
            }
        }