builder.go (430 lines of code) (raw):
package phonenumbers
import (
"encoding/xml"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
)
// ----------------------------------------------------------------------------
// Golang port of:
// https://github.com/googlei18n/libphonenumber/blob/master/tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java
// ----------------------------------------------------------------------------
func sp(value string) *string {
if value == "" {
return nil
}
return &value
}
func bp(value bool) *bool {
return &value
}
func ip(value int32) *int32 {
return &value
}
func BuildPhoneMetadataCollection(inputXML []byte, liteBuild bool, specialBuild bool, isShortNumberMetadata bool) (*PhoneMetadataCollection, error) {
metadata := &PhoneNumberMetadataE{}
err := xml.Unmarshal(inputXML, metadata)
if err != nil {
panic(fmt.Sprintf("Error unmarshalling XML: %s", err))
}
isAlternateFormatsMetadata := false
return buildPhoneMetadataFromElement(metadata, liteBuild, specialBuild, isShortNumberMetadata, isAlternateFormatsMetadata)
}
func buildPhoneMetadataFromElement(document *PhoneNumberMetadataE, liteBuild bool, specialBuild bool, isShortNumberMetadata bool, isAlternateFormatsMetadata bool) (*PhoneMetadataCollection, error) {
collection := PhoneMetadataCollection{}
numOfTerritories := len(document.Territories)
for i := 0; i < numOfTerritories; i++ {
territoryElement := document.Territories[i]
regionCode := territoryElement.ID
metadata := loadCountryMetadata(regionCode, &territoryElement, isShortNumberMetadata, isAlternateFormatsMetadata)
collection.Metadata = append(collection.Metadata, metadata)
}
return &collection, nil
}
// Build a mapping from a country calling code to the region codes which denote the country/region
// represented by that country code. In the case of multiple countries sharing a calling code,
// such as the NANPA countries, the one indicated with "isMainCountryForCode" in the metadata
// should be first.
func BuildCountryCodeToRegionMap(metadataCollection *PhoneMetadataCollection) map[int][]string {
countryCodeToRegionCodeMap := make(map[int][]string)
for _, metadata := range metadataCollection.Metadata {
regionCode := metadata.GetId()
countryCode := int(metadata.GetCountryCode())
_, present := countryCodeToRegionCodeMap[countryCode]
if present {
phoneList := countryCodeToRegionCodeMap[countryCode]
if metadata.GetMainCountryForCode() {
phoneList = append([]string{regionCode}, phoneList...)
} else {
phoneList = append(phoneList, regionCode)
}
countryCodeToRegionCodeMap[countryCode] = phoneList
} else {
// For most countries, there will be only one region code for the country calling code.
phoneList := []string{}
if regionCode != "" { // For alternate formats, there are no region codes at all.
phoneList = append(phoneList, regionCode)
}
countryCodeToRegionCodeMap[countryCode] = phoneList
}
}
return countryCodeToRegionCodeMap
}
func validateRE(re string, removeWhitespace bool) string {
// Removes all the whitespace and newline from the regexp. Not Ming pattern compile options to
// make it work across programming languages.
if removeWhitespace {
re = string(regexp.MustCompile(`\s`).ReplaceAllLiteralString(re, ""))
}
_, err := regexp.Compile(re)
if err != nil {
panic(err)
}
return re
}
func loadTerritoryTagMetadata(regionCode string, territory *TerritoryE, nationalPrefix string) *PhoneMetadata {
metadata := &PhoneMetadata{}
metadata.Id = sp(regionCode)
if territory.CountryCode != 0 {
metadata.CountryCode = ip(territory.CountryCode)
}
if territory.LeadingDigits != "" {
metadata.LeadingDigits = sp(validateRE(territory.LeadingDigits, false))
}
if territory.InternationalPrefix != "" {
metadata.InternationalPrefix = sp(validateRE(territory.InternationalPrefix, false))
}
if territory.PreferredInternationalPrefix != "" {
metadata.PreferredInternationalPrefix = sp(territory.PreferredInternationalPrefix)
}
if territory.NationalPrefixForParsing != "" {
metadata.NationalPrefixForParsing = sp(validateRE(territory.NationalPrefixForParsing, true))
if territory.NationalPrefixTransformRule != "" {
metadata.NationalPrefixTransformRule = sp(validateRE(territory.NationalPrefixTransformRule, false))
}
}
if nationalPrefix != "" {
metadata.NationalPrefix = sp(nationalPrefix)
if metadata.NationalPrefixForParsing == nil {
metadata.NationalPrefixForParsing = sp(nationalPrefix)
}
}
if territory.PreferredExtnPrefix != "" {
metadata.PreferredExtnPrefix = sp(territory.PreferredExtnPrefix)
}
if territory.MainCountryForCode {
metadata.MainCountryForCode = bp(true)
}
if territory.MobileNumberPortableRegion {
metadata.MobileNumberPortableRegion = bp(true)
}
return metadata
}
func setLeadingDigitsPatterns(numberFormatElement *NumberFormatE, format *NumberFormat) {
if len(numberFormatElement.LeadingDigits) > 0 {
for i := 0; i < len(numberFormatElement.LeadingDigits); i++ {
format.LeadingDigitsPattern = append(format.LeadingDigitsPattern, validateRE(numberFormatElement.LeadingDigits[i], 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.
*/
func loadInternationalFormat(metadata *PhoneMetadata, numberFormatElement *NumberFormatE, nationalFormat *NumberFormat) bool {
intlFormat := &NumberFormat{}
intlFormatPattern := numberFormatElement.InternationalFormat
hasExplicitIntlFormatDefined := false
if len(intlFormatPattern) > 1 {
panic("Invalid number of intlFormat patterns for country: " + metadata.GetId())
} else if len(intlFormatPattern) == 0 {
// Default to use the same as the national pattern if none is defined.
intlFormat.merge(nationalFormat)
} else {
intlFormat.Pattern = sp(numberFormatElement.Pattern)
setLeadingDigitsPatterns(numberFormatElement, intlFormat)
intlFormatPatternValue := intlFormatPattern[0]
if intlFormatPatternValue != "NA" {
intlFormat.Format = sp(intlFormatPatternValue)
}
hasExplicitIntlFormatDefined = true
}
if intlFormat.Format != nil {
metadata.IntlNumberFormat = append(metadata.IntlNumberFormat, intlFormat)
}
return hasExplicitIntlFormatDefined
}
/**
* Extracts the pattern for the national format.
*
* @throws RuntimeException if multiple or no formats have been encountered.
*/
// @VisibleForTesting
func loadNationalFormat(metadata *PhoneMetadata, numberFormatElement *NumberFormatE, format *NumberFormat) {
setLeadingDigitsPatterns(numberFormatElement, format)
format.Pattern = sp(validateRE(numberFormatElement.Pattern, false))
format.Format = sp(numberFormatElement.Format)
}
func getDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule string, nationalPrefix string) string {
// Replace $FG with the first group ($1) and $NP with the national prefix.
carrierCodeFormattingRule = strings.Replace(carrierCodeFormattingRule, "$FG", "$1", 1)
carrierCodeFormattingRule = strings.Replace(carrierCodeFormattingRule, "$NP", nationalPrefix, 1)
return carrierCodeFormattingRule
}
func getNationalPrefixFormattingRule(nationalPrefixFormattingRule string, nationalPrefix string) string {
// Replace $NP with national prefix and $FG with the first group ($1).
nationalPrefixFormattingRule = strings.Replace(nationalPrefixFormattingRule, "$NP", nationalPrefix, 1)
nationalPrefixFormattingRule = strings.Replace(nationalPrefixFormattingRule, "$FG", "$1", 1)
return nationalPrefixFormattingRule
}
/**
* Extracts the available formats from the provided DOM element. If it does not contain any
* nationalPrefixFormattingRule, the one passed-in is retained; similarly for
* nationalPrefixOptionalWhenFormatting. The nationalPrefix, nationalPrefixFormattingRule and
* nationalPrefixOptionalWhenFormatting values are provided from the parent (territory) element.
*/
// @VisibleForTesting
func loadAvailableFormats(metadata *PhoneMetadata, element *TerritoryE, nationalPrefix string,
nationalPrefixFormattingRule string, nationalPrefixOptionalWhenFormatting bool) {
carrierCodeFormattingRule := ""
if element.CarrierCodeFormattingRule != "" {
carrierCodeFormattingRule = validateRE(getDomesticCarrierCodeFormattingRule(element.CarrierCodeFormattingRule, nationalPrefix), false)
}
numberFormatElements := element.AvailableFormats
hasExplicitIntlFormatDefined := false
if len(numberFormatElements) > 0 {
for i := 0; i < len(numberFormatElements); i++ {
numberFormatElement := numberFormatElements[i]
format := NumberFormat{}
if numberFormatElement.NationalPrefixFormattingRule != "" {
format.NationalPrefixFormattingRule = sp(getNationalPrefixFormattingRule(numberFormatElement.NationalPrefixFormattingRule, nationalPrefix))
} else {
format.NationalPrefixFormattingRule = sp(nationalPrefixFormattingRule)
}
if numberFormatElement.NationalPrefixOptionalWhenFormatting != nil {
format.NationalPrefixOptionalWhenFormatting = numberFormatElement.NationalPrefixOptionalWhenFormatting
} else if nationalPrefixOptionalWhenFormatting {
format.NationalPrefixOptionalWhenFormatting = bp(nationalPrefixOptionalWhenFormatting)
}
if numberFormatElement.CarrierCodeFormattingRule != "" {
format.DomesticCarrierCodeFormattingRule = sp(validateRE(getDomesticCarrierCodeFormattingRule(numberFormatElement.CarrierCodeFormattingRule, nationalPrefix), false))
} else if carrierCodeFormattingRule != "" {
format.DomesticCarrierCodeFormattingRule = sp(carrierCodeFormattingRule)
}
loadNationalFormat(metadata, &numberFormatElement, &format)
metadata.NumberFormat = append(metadata.NumberFormat, &format)
if loadInternationalFormat(metadata, &numberFormatElement, &format) {
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.IntlNumberFormat = nil
}
}
}
/**
* 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.
*/
func arePossibleLengthsEqual(possibleLengths map[int32]bool, desc *PhoneNumberDesc) bool {
if len(possibleLengths) != len(desc.PossibleLength) {
return false
}
// check whether the same elements exist
for _, val := range desc.PossibleLength {
_, exists := possibleLengths[val]
if !exists {
return false
}
}
return true
}
/**
* Parses a possible length string into a set of the integers that are covered.
*
* @param possibleLengthString a string specifying the possible lengths of phone numbers. Follows
* this syntax: ranges or elements are separated by commas, and ranges are specified in
* [min-max] notation, inclusive. For example, [3-5],7,9,[11-14] should be parsed to
* 3,4,5,7,9,11,12,13,14.
*/
func parsePossibleLengthStringToSet(possibleLengthString string) map[int32]bool {
if possibleLengthString == "" {
panic("Empty possibleLength string found.")
}
lengths := strings.Split(possibleLengthString, ",")
lengthSet := make(map[int32]bool)
for i := 0; i < len(lengths); i++ {
lengthSubstring := lengths[i]
if lengthSubstring == "" {
panic("Leading, trailing or adjacent commas in possible length string %s, these should only separate numbers or ranges.")
} else if lengthSubstring[0] == '[' {
if lengthSubstring[len(lengthSubstring)-1] != ']' {
panic(fmt.Sprintf("Missing end of range character in possible length string %s.", possibleLengthString))
}
// Strip the leading and trailing [], and split on the -.
minMax := strings.Split(lengthSubstring[1:len(lengthSubstring)-1], "-")
if len(minMax) != 2 {
panic(fmt.Sprintf("Ranges must have exactly one - character: missing for %s.", possibleLengthString))
}
min, _ := strconv.Atoi(minMax[0])
max, _ := strconv.Atoi(minMax[1])
// We don't even accept [6-7] since we prefer the shorter 6,7 variant; for a range to be in
// use the hyphen needs to replace at least one digit.
if max-min < 2 {
panic(fmt.Sprintf("The first number in a range should be two or more digits lower than the second. Culprit possibleLength string: %s", possibleLengthString))
}
for j := min; j <= max; j++ {
lengthSet[int32(j)] = true
}
} else {
length, _ := strconv.Atoi(lengthSubstring)
lengthSet[int32(length)] = true
}
}
return lengthSet
}
/**
* Reads the possible lengths present in the metadata and splits them into two sets: one for
* full-length numbers, one for local numbers.
*
* @param data one or more phone number descriptions, represented as XML nodes
* @param lengths a set to which to add possible lengths of full phone numbers
* @param localOnlyLengths a set to which to add possible lengths of phone numbers only diallable
* locally (e.g. within a province)
*/
func populatePossibleLengthSets(data []*PhoneNumberDescE, lengths map[int32]bool, localOnlyLengths map[int32]bool) {
for i := 0; i < len(data); i++ {
desc := data[i]
if desc == nil || desc.PossibleLengths == nil {
continue
}
element := desc.PossibleLengths
nationalLengths := element.National
// We don't add to the phone metadata yet, since we want to sort length elements found under
// different nodes first, make sure there are no duplicates between them and that the
// localOnly lengths don't overlap with the others.
thisElementLengths := parsePossibleLengthStringToSet(nationalLengths)
if element.LocalOnly != "" {
thisElementLocalOnlyLengths := parsePossibleLengthStringToSet(element.LocalOnly)
// intersect our two maps
intersection := make(map[int32]bool)
for k := range thisElementLengths {
if thisElementLocalOnlyLengths[k] {
intersection[k] = true
}
}
if len(intersection) != 0 {
panic(fmt.Sprintf("Possible length(s) found specified as a normal and local-only length: %v", intersection))
}
// We check again when we set these lengths on the metadata itself in setPossibleLengths
// that the elements in localOnly are not also in lengths. For e.g. the generalDesc, it
// might have a local-only length for one type that is a normal length for another type. We
// don't consider this an error, but we do want to remove the local-only lengths.
for k := range thisElementLocalOnlyLengths {
localOnlyLengths[k] = true
}
}
// It is okay if at this time we have duplicates, because the same length might be possible
// for e.g. fixed-line and for mobile numbers, and this method operates potentially on
// multiple phoneNumberDesc XML elements.
for k := range thisElementLengths {
lengths[k] = true
}
}
}
/**
* 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. Note that the parent description
* must therefore already be processed before this method is called on any child elements.
*
* @param parentDesc a generic phone number description that will be used to fill in missing
* parts of the description, or null if this is the root node. This must be processed before
* this is run on any child elements.
* @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
*/
// @VisibleForTesting
func processPhoneNumberDescElement(parentDesc *PhoneNumberDesc, element *PhoneNumberDescE) *PhoneNumberDesc {
numberDesc := PhoneNumberDesc{}
if element == nil {
numberDesc.NationalNumberPattern = sp("NA")
return &numberDesc
}
if parentDesc != nil {
// 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).
lengths := make(map[int32]bool)
localOnlyLengths := make(map[int32]bool)
populatePossibleLengthSets([]*PhoneNumberDescE{element}, lengths, localOnlyLengths)
setPossibleLengths(lengths, localOnlyLengths, parentDesc, &numberDesc)
}
validPattern := element.NationalNumberPattern
if validPattern != "" {
numberDesc.NationalNumberPattern = sp(validateRE(validPattern, true))
}
exampleNumber := element.ExampleNumber
if exampleNumber != "" {
numberDesc.ExampleNumber = sp(exampleNumber)
}
return &numberDesc
}
/**
* 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
*/
func setPossibleLengths(lengths map[int32]bool, localOnlyLengths map[int32]bool, parentDesc *PhoneNumberDesc, desc *PhoneNumberDesc) {
// We clear these fields since the metadata tends to inherit from the parent element for other
// fields (via a mergeFrom).
desc.PossibleLength = nil
desc.PossibleLengthLocalOnly = nil
// 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 == nil || !arePossibleLengthsEqual(lengths, parentDesc) {
for length := range lengths {
if parentDesc == nil || parentDesc.hasPossibleLength(length) {
desc.PossibleLength = append(desc.PossibleLength, 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.
panic(fmt.Sprintf("Out-of-range possible length found (%d), parent lengths %v.", length, parentDesc.PossibleLength))
}
}
}
// 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.
for length := range localOnlyLengths {
if !lengths[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 == nil || parentDesc.hasPossibleLength(length) || parentDesc.hasPossibleLengthLocalOnly(length) {
desc.PossibleLengthLocalOnly = append(desc.PossibleLengthLocalOnly, length)
} else {
panic(fmt.Sprintf("Out-of-range local-only possible length found (%d), parent length %v.", length, parentDesc.PossibleLengthLocalOnly))
}
}
}
// Need to sort both lists, possible lengths need to be ordered
sort.Slice(desc.PossibleLength, func(i, j int) bool { return desc.PossibleLength[i] < desc.PossibleLength[j] })
sort.Slice(desc.PossibleLengthLocalOnly, func(i, j int) bool { return desc.PossibleLengthLocalOnly[i] < desc.PossibleLengthLocalOnly[j] })
}
/**
* Sets possible lengths in the general description, derived from certain child elements.
*/
func setPossibleLengthsGeneralDesc(generalDesc *PhoneNumberDesc, metadataId string, data *TerritoryE, isShortNumberMetadata bool) {
lengths := make(map[int32]bool)
localOnlyLengths := make(map[int32]bool)
// 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).
generalDescNode := data.GeneralDesc
populatePossibleLengthSets([]*PhoneNumberDescE{generalDescNode}, lengths, localOnlyLengths)
if len(lengths) != 0 || len(localOnlyLengths) != 0 {
// We shouldn't have anything specified at the "general desc" level: we are going to
// calculate this ourselves from child elements.
panic(fmt.Sprintf("Found possible lengths specified at general desc: this should be derived from child elements. Affected country: %s", 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.
// We remove no-international dialing
trimmedDescs := []*PhoneNumberDescE{data.GeneralDesc, data.FixedLine, data.Mobile, data.Pager,
data.TollFree, data.PremiumRate, data.SharedCost, data.PersonalNumber, data.VOIP, data.UAN, data.VoiceMail, data.StandardRate,
data.ShortCode, data.Emergency, data.CarrierSpecific}
populatePossibleLengthSets(trimmedDescs, lengths, localOnlyLengths)
} else {
populatePossibleLengthSets([]*PhoneNumberDescE{data.ShortCode}, lengths, localOnlyLengths)
if len(localOnlyLengths) > 0 {
panic(fmt.Errorf("found local-only lengths in short-number metadata"))
}
}
setPossibleLengths(lengths, localOnlyLengths, nil, generalDesc)
}
func loadCountryMetadata(regionCode string, element *TerritoryE, isShortNumberMetadata bool, isAlternateFormatsMetadata bool) *PhoneMetadata {
nationalPrefix := element.NationalPrefix
metadata := loadTerritoryTagMetadata(regionCode, element, nationalPrefix)
nationalPrefixFormattingRule := getNationalPrefixFormattingRule(element.NationalPrefixFormattingRule, nationalPrefix)
loadAvailableFormats(metadata, element, nationalPrefix, nationalPrefixFormattingRule, element.NationalPrefixOptionalWhenFormatting)
if !isAlternateFormatsMetadata {
// The alternate formats metadata does not need most of the patterns to be set.
setRelevantDescPatterns(metadata, element, isShortNumberMetadata)
}
return metadata
}
func setRelevantDescPatterns(metadata *PhoneMetadata, element *TerritoryE, isShortNumberMetadata bool) {
generalDesc := processPhoneNumberDescElement(nil, element.GeneralDesc)
// Calculate the possible lengths for the general description. This will be based on the
// possible lengths of the child elements.
setPossibleLengthsGeneralDesc(generalDesc, metadata.GetId(), element, isShortNumberMetadata)
metadata.GeneralDesc = generalDesc
if !isShortNumberMetadata {
// Set fields used by regular length phone numbers.
metadata.FixedLine = processPhoneNumberDescElement(generalDesc, element.FixedLine)
metadata.Mobile = processPhoneNumberDescElement(generalDesc, element.Mobile)
metadata.SharedCost = processPhoneNumberDescElement(generalDesc, element.SharedCost)
metadata.Voip = processPhoneNumberDescElement(generalDesc, element.VOIP)
metadata.PersonalNumber = processPhoneNumberDescElement(generalDesc, element.PersonalNumber)
metadata.Pager = processPhoneNumberDescElement(generalDesc, element.Pager)
metadata.Uan = processPhoneNumberDescElement(generalDesc, element.UAN)
metadata.Voicemail = processPhoneNumberDescElement(generalDesc, element.VoiceMail)
metadata.NoInternationalDialling = processPhoneNumberDescElement(generalDesc, element.NoInternationalDialing)
mobileAndFixedAreSame := *metadata.Mobile.NationalNumberPattern == *metadata.FixedLine.NationalNumberPattern
if metadata.GetSameMobileAndFixedLinePattern() != mobileAndFixedAreSame {
metadata.SameMobileAndFixedLinePattern = bp(mobileAndFixedAreSame)
}
metadata.TollFree = processPhoneNumberDescElement(generalDesc, element.TollFree)
metadata.PremiumRate = processPhoneNumberDescElement(generalDesc, element.PremiumRate)
} else {
// Set fields used by short numbers.
metadata.StandardRate = processPhoneNumberDescElement(generalDesc, element.StandardRate)
metadata.ShortCode = processPhoneNumberDescElement(generalDesc, element.ShortCode)
metadata.CarrierSpecific = processPhoneNumberDescElement(generalDesc, element.CarrierSpecific)
metadata.Emergency = processPhoneNumberDescElement(generalDesc, element.Emergency)
metadata.TollFree = processPhoneNumberDescElement(generalDesc, element.TollFree)
metadata.PremiumRate = processPhoneNumberDescElement(generalDesc, element.PremiumRate)
}
}
// <!ELEMENT phoneNumberMetadata (territories)>
type PhoneNumberMetadataE struct {
// <!ELEMENT territories (territory+)>
Territories []TerritoryE `xml:"territories>territory"`
}
// <!ELEMENT territory (references?, availableFormats?, generalDesc, noInternationalDialling?,
//fixedLine?, mobile?, pager?, tollFree?, premiumRate?,
//sharedCost?, personalNumber?, voip?, uan?, voicemail?)>
type TerritoryE struct {
// <!ATTLIST territory id CDATA #REQUIRED>
ID string `xml:"id,attr"`
// <!ATTLIST territory mainCountryForCode (true) #IMPLIED>
MainCountryForCode bool `xml:"mainCountryForCode,attr"`
// <!ATTLIST territory leadingDigits CDATA #IMPLIED>
LeadingDigits string `xml:"leadingDigits,attr"`
// <!ATTLIST territory countryCode CDATA #REQUIRED>
CountryCode int32 `xml:"countryCode,attr"`
// <!ATTLIST territory nationalPrefix CDATA #IMPLIED>
NationalPrefix string `xml:"nationalPrefix,attr"`
// <!ATTLIST territory internationalPrefix CDATA #IMPLIED>
InternationalPrefix string `xml:"internationalPrefix,attr"`
// <!ATTLIST territory preferredInternationalPrefix CDATA #IMPLIED>
PreferredInternationalPrefix string `xml:"preferredInternationalPrefix,attr"`
// <!ATTLIST territory nationalPrefixFormattingRule CDATA #IMPLIED>
NationalPrefixFormattingRule string `xml:"nationalPrefixFormattingRule,attr"`
// <!ATTLIST territory mobileNumberPortableRegion (true) #IMPLIED>
MobileNumberPortableRegion bool `xml:"mobileNumberPortableRegion,attr"`
// <!ATTLIST territory nationalPrefixForParsing CDATA #IMPLIED>
NationalPrefixForParsing string `xml:"nationalPrefixForParsing,attr"`
// <!ATTLIST territory nationalPrefixTransformRule CDATA #IMPLIED>
NationalPrefixTransformRule string `xml:"nationalPrefixTransformRule,attr"`
// <!ATTLIST territory preferredExtnPrefix CDATA #IMPLIED>
PreferredExtnPrefix string `xml:"PreferredExtnPrefix"`
// <!ATTLIST territory nationalPrefixOptionalWhenFormatting (true) #IMPLIED>
NationalPrefixOptionalWhenFormatting bool `xml:"nationalPrefixOptionalWhenFormatting,attr"`
// <!ATTLIST territory carrierCodeFormattingRule CDATA #IMPLIED>
CarrierCodeFormattingRule string `xml:"carrierCodeFormattingRule,attr"`
// <!ELEMENT references (sourceUrl+)>
// <!ELEMENT sourceUrl (#PCDATA)>
References []string `xml:"references>sourceUrl"`
// <!ELEMENT availableFormats (numberFormat+)>
AvailableFormats []NumberFormatE `xml:"availableFormats>numberFormat"`
// <!ELEMENT generalDesc (nationalNumberPattern)>
GeneralDesc *PhoneNumberDescE `xml:"generalDesc"`
// <!ELEMENT noInternationalDialling (nationalNumberPattern, possibleLengths, exampleNumber)>
NoInternationalDialing *PhoneNumberDescE `xml:"noInternationalDialing"`
// <!ELEMENT fixedLine (nationalNumberPattern, possibleLengths, exampleNumber)>
FixedLine *PhoneNumberDescE `xml:"fixedLine"`
// <!ELEMENT mobile (nationalNumberPattern, possibleLengths, exampleNumber)>
Mobile *PhoneNumberDescE `xml:"mobile"`
// <!ELEMENT pager (nationalNumberPattern, possibleLengths, exampleNumber)>
Pager *PhoneNumberDescE `xml:"pager"`
// <!ELEMENT tollFree (nationalNumberPattern, possibleLengths, exampleNumber)>
TollFree *PhoneNumberDescE `xml:"tollFree"`
// <!ELEMENT premiumRate (nationalNumberPattern, possibleLengths, exampleNumber)>
PremiumRate *PhoneNumberDescE `xml:"premiumRate"`
// <!ELEMENT sharedCost (nationalNumberPattern, possibleLengths, exampleNumber)>
SharedCost *PhoneNumberDescE `xml:"sharedCost"`
// <!ELEMENT personalNumber (nationalNumberPattern, possibleLengths, exampleNumber)>
PersonalNumber *PhoneNumberDescE `xml:"personalNumber"`
// <!ELEMENT voip (nationalNumberPattern, possibleLengths, exampleNumber)>
VOIP *PhoneNumberDescE `xml:"voip"`
// <!ELEMENT uan (nationalNumberPattern, possibleLengths, exampleNumber)>
UAN *PhoneNumberDescE `xml:"uan"`
// <!ELEMENT voicemail (nationalNumberPattern, possibleLengths, exampleNumber)>
VoiceMail *PhoneNumberDescE `xml:"voicemail"`
// <!ELEMENT uan (nationalNumberPattern, possibleLengths, exampleNumber)>
StandardRate *PhoneNumberDescE `xml:"standardRate"`
// <!ELEMENT voicemail (nationalNumberPattern, possibleLengths, exampleNumber)>
ShortCode *PhoneNumberDescE `xml:"shortCode"`
// <!ELEMENT uan (nationalNumberPattern, possibleLengths, exampleNumber)>
Emergency *PhoneNumberDescE `xml:"emergency"`
// <!ELEMENT voicemail (nationalNumberPattern, possibleLengths, exampleNumber)>
CarrierSpecific *PhoneNumberDescE `xml:"carrierSpecific"`
}
// <!ELEMENT numberFormat (leadingDigits*, format, intlFormat*)>
type NumberFormatE struct {
// <!ELEMENT leadingDigits (#PCDATA)>
LeadingDigits []string `xml:"leadingDigits"`
// <!ELEMENT format (#PCDATA)>
Format string `xml:"format"`
// <!ELEMENT intlFormat (#PCDATA)>
InternationalFormat []string `xml:"intlFormat"`
// <!ATTLIST numberFormat nationalPrefixFormattingRule CDATA #IMPLIED>
NationalPrefixFormattingRule string `xml:"nationalPrefixFormattingRule,attr"`
// <!ATTLIST numberFormat nationalPrefixOptionalWhenFormatting (true) #IMPLIED>
NationalPrefixOptionalWhenFormatting *bool `xml:"nationalPrefixOptionalWhenFormatting,attr"`
// <!ATTLIST numberFormat carrierCodeFormattingRule CDATA #IMPLIED>
CarrierCodeFormattingRule string `xml:"carrierCodeFormattingRule,attr"`
// <!ATTLIST numberFormat pattern CDATA #REQUIRED>
Pattern string `xml:"pattern,attr" validate:"required"`
}
type PossibleLengthE struct {
// <!ATTLIST possibleLengths national CDATA #REQUIRED>
National string `xml:"national,attr"`
// <!ATTLIST possibleLengths localOnly CDATA #IMPLIED>
LocalOnly string `xml:"localOnly,attr"`
}
type PhoneNumberDescE struct {
// <!ELEMENT nationalNumberPattern (#PCDATA)>
NationalNumberPattern string `xml:"nationalNumberPattern"`
// <!ELEMENT possibleLengths EMPTY>
PossibleLengths *PossibleLengthE `xml:"possibleLengths"`
// <!ELEMENT exampleNumber (#PCDATA)>
ExampleNumber string `xml:"exampleNumber"`
}