1212 lines
42 KiB
Objective-C
1212 lines
42 KiB
Objective-C
//
|
||
// NBAsYouTypeFormatter.m
|
||
// libPhoneNumber
|
||
//
|
||
// Created by ishtar on 13. 2. 25..
|
||
//
|
||
|
||
#import "NBAsYouTypeFormatter.h"
|
||
#import "NBPhoneNumberDefines.h"
|
||
|
||
#import "NBMetadataHelper.h"
|
||
|
||
#import "NBNumberFormat.h"
|
||
#import "NBPhoneMetaData.h"
|
||
#import "NBPhoneNumberUtil.h"
|
||
#import "NSArray+NBAdditions.h"
|
||
|
||
/**
|
||
* The digits that have not been entered yet will be represented by a \u2008,
|
||
* the punctuation space.
|
||
*/
|
||
static NSString *const NBDigitPlaceHolder = @"\u2008";
|
||
/**
|
||
* Character used when appropriate to separate a prefix, such as a long NDD or a
|
||
* country calling code, from the national number.
|
||
*/
|
||
static NSString *const NBSeparatorBeforeNationalNumber = @" ";
|
||
|
||
/**
|
||
* This is the minimum length of national number accrued that is required to
|
||
* trigger the formatter. The first element of the leadingDigitsPattern of
|
||
* each numberFormat contains a regular expression that matches up to this
|
||
* number of digits.
|
||
*/
|
||
static const NSUInteger NBMinLeadingDigitsLength = 3;
|
||
|
||
@interface NBAsYouTypeFormatter ()
|
||
|
||
@property(nonatomic, strong, readwrite) NSString *currentOutput_, *currentFormattingPattern_;
|
||
@property(nonatomic, strong, readwrite) NSString *defaultCountry_;
|
||
@property(nonatomic, strong, readwrite) NSString *nationalPrefixExtracted_;
|
||
@property(nonatomic, strong, readwrite) NSMutableString *formattingTemplate_, *accruedInput_,
|
||
*prefixBeforeNationalNumber_, *accruedInputWithoutFormatting_, *nationalNumber_;
|
||
@property(nonatomic, strong, readwrite) NSRegularExpression *DIGIT_PATTERN_,
|
||
*NATIONAL_PREFIX_SEPARATORS_PATTERN_, *CHARACTER_CLASS_PATTERN_, *STANDALONE_DIGIT_PATTERN_;
|
||
@property(nonatomic, strong, readwrite) NSRegularExpression *ELIGIBLE_FORMAT_PATTERN_;
|
||
@property(nonatomic, assign, readwrite) BOOL ableToFormat_, inputHasFormatting_, isCompleteNumber_,
|
||
isExpectingCountryCallingCode_, shouldAddSpaceAfterNationalPrefix_;
|
||
@property(nonatomic, strong, readwrite) NBPhoneNumberUtil *phoneUtil_;
|
||
@property(nonatomic, assign, readwrite) NSUInteger lastMatchPosition_, originalPosition_,
|
||
positionToRemember_;
|
||
@property(nonatomic, strong, readwrite) NSMutableArray *possibleFormats_;
|
||
@property(nonatomic, strong, readwrite) NBPhoneMetaData *currentMetaData_, *defaultMetaData_;
|
||
|
||
@end
|
||
|
||
@implementation NBAsYouTypeFormatter
|
||
|
||
- (instancetype)init {
|
||
self = [super init];
|
||
|
||
if (self) {
|
||
_isSuccessfulFormatting = NO;
|
||
|
||
/**
|
||
* @type {string}
|
||
* @private
|
||
*/
|
||
self.currentOutput_ = @"";
|
||
|
||
/**
|
||
* @type {!goog.string.StringBuffer}
|
||
* @private
|
||
*/
|
||
self.formattingTemplate_ = [[NSMutableString alloc] init];
|
||
|
||
NSError *anError = nil;
|
||
|
||
/**
|
||
* @type {RegExp}
|
||
* @private
|
||
*/
|
||
self.DIGIT_PATTERN_ = [NSRegularExpression regularExpressionWithPattern:NBDigitPlaceHolder
|
||
options:0
|
||
error:&anError];
|
||
|
||
/**
|
||
* A set of characters that, if found in a national prefix formatting rules, are
|
||
* an indicator to us that we should separate the national prefix from the
|
||
* number when formatting.
|
||
* @const
|
||
* @type {RegExp}
|
||
* @private
|
||
*/
|
||
self.NATIONAL_PREFIX_SEPARATORS_PATTERN_ =
|
||
[NSRegularExpression regularExpressionWithPattern:@"[- ]" options:0 error:&anError];
|
||
|
||
/**
|
||
* A pattern that is used to match character classes in regular expressions.
|
||
* An example of a character class is [1-4].
|
||
* @const
|
||
* @type {RegExp}
|
||
* @private
|
||
*/
|
||
self.CHARACTER_CLASS_PATTERN_ =
|
||
[NSRegularExpression regularExpressionWithPattern:@"\\[([^\\[\\]])*\\]"
|
||
options:0
|
||
error:&anError];
|
||
|
||
/**
|
||
* Any digit in a regular expression that actually denotes a digit. For
|
||
* example, in the regular expression 80[0-2]\d{6,10}, the first 2 digits
|
||
* (8 and 0) are standalone digits, but the rest are not.
|
||
* Two look-aheads are needed because the number following \\d could be a
|
||
* two-digit number, since the phone number can be as long as 15 digits.
|
||
* @const
|
||
* @type {RegExp}
|
||
* @private
|
||
*/
|
||
self.STANDALONE_DIGIT_PATTERN_ =
|
||
[NSRegularExpression regularExpressionWithPattern:@"\\d(?=[^,}][^,}])"
|
||
options:0
|
||
error:&anError];
|
||
|
||
/**
|
||
* A pattern that is used to determine if a numberFormat under availableFormats
|
||
* is eligible to be used by the AYTF. It is eligible when the format element
|
||
* under numberFormat contains groups of the dollar sign followed by a single
|
||
* digit, separated by valid phone number punctuation. This prevents invalid
|
||
* punctuation (such as the star sign in Israeli star numbers) getting into the
|
||
* output of the AYTF.
|
||
* @const
|
||
* @type {RegExp}
|
||
* @private
|
||
*/
|
||
NSString *eligible_format =
|
||
@"^[-x‐-―−ー--/ ()()[].\\[\\]/~⁓∼~]*(\\$\\d[-x‐-―−ー--/ "
|
||
@" ()()[].\\[\\]/~⁓∼~]*)+$";
|
||
self.ELIGIBLE_FORMAT_PATTERN_ =
|
||
[NSRegularExpression regularExpressionWithPattern:eligible_format options:0 error:&anError];
|
||
|
||
/**
|
||
* The pattern from numberFormat that is currently used to create
|
||
* formattingTemplate.
|
||
* @type {string}
|
||
* @private
|
||
*/
|
||
self.currentFormattingPattern_ = @"";
|
||
|
||
/**
|
||
* @type {!goog.string.StringBuffer}
|
||
* @private
|
||
*/
|
||
self.accruedInput_ = [[NSMutableString alloc] init];
|
||
|
||
/**
|
||
* @type {!goog.string.StringBuffer}
|
||
* @private
|
||
*/
|
||
self.accruedInputWithoutFormatting_ = [[NSMutableString alloc] init];
|
||
|
||
/**
|
||
* This indicates whether AsYouTypeFormatter is currently doing the
|
||
* formatting.
|
||
* @type {BOOL}
|
||
* @private
|
||
*/
|
||
self.ableToFormat_ = YES;
|
||
|
||
/**
|
||
* Set to YES when users enter their own formatting. AsYouTypeFormatter will
|
||
* do no formatting at all when this is set to YES.
|
||
* @type {BOOL}
|
||
* @private
|
||
*/
|
||
self.inputHasFormatting_ = NO;
|
||
|
||
/**
|
||
* This is set to YES when we know the user is entering a full national
|
||
* significant number, since we have either detected a national prefix or an
|
||
* international dialing prefix. When this is YES, we will no longer use
|
||
* local number formatting patterns.
|
||
* @type {BOOL}
|
||
* @private
|
||
*/
|
||
self.isCompleteNumber_ = NO;
|
||
|
||
/**
|
||
* @type {BOOL}
|
||
* @private
|
||
*/
|
||
self.isExpectingCountryCallingCode_ = NO;
|
||
|
||
/**
|
||
* @type {number}
|
||
* @private
|
||
*/
|
||
self.lastMatchPosition_ = 0;
|
||
|
||
/**
|
||
* The position of a digit upon which inputDigitAndRememberPosition is most
|
||
* recently invoked, as found in the original sequence of characters the user
|
||
* entered.
|
||
* @type {number}
|
||
* @private
|
||
*/
|
||
self.originalPosition_ = 0;
|
||
|
||
/**
|
||
* The position of a digit upon which inputDigitAndRememberPosition is most
|
||
* recently invoked, as found in accruedInputWithoutFormatting.
|
||
* entered.
|
||
* @type {number}
|
||
* @private
|
||
*/
|
||
self.positionToRemember_ = 0;
|
||
|
||
/**
|
||
* This contains anything that has been entered so far preceding the national
|
||
* significant number, and it is formatted (e.g. with space inserted). For
|
||
* example, this can contain IDD, country code, and/or NDD, etc.
|
||
* @type {!goog.string.StringBuffer}
|
||
* @private
|
||
*/
|
||
self.prefixBeforeNationalNumber_ = [[NSMutableString alloc] init];
|
||
|
||
/**
|
||
* @type {BOOL}
|
||
* @private
|
||
*/
|
||
self.shouldAddSpaceAfterNationalPrefix_ = NO;
|
||
|
||
/**
|
||
* This contains the national prefix that has been extracted. It contains only
|
||
* digits without formatting.
|
||
* @type {string}
|
||
* @private
|
||
*/
|
||
self.nationalPrefixExtracted_ = @"";
|
||
|
||
/**
|
||
* @type {!goog.string.StringBuffer}
|
||
* @private
|
||
*/
|
||
self.nationalNumber_ = [[NSMutableString alloc] init];
|
||
|
||
/**
|
||
* @type {Array.<i18n.phonenumbers.NumberFormat>}
|
||
* @private
|
||
*/
|
||
self.possibleFormats_ = [[NSMutableArray alloc] init];
|
||
}
|
||
|
||
return self;
|
||
}
|
||
|
||
/**
|
||
* Constructs an AsYouTypeFormatter for the specific region.
|
||
*
|
||
* - param regionCode regionCode the ISO 3166-1 two-letter region code that denotes
|
||
* the region where the phone number is being entered.
|
||
* @constructor
|
||
*/
|
||
|
||
- (instancetype)initWithRegionCode:(NSString *)regionCode {
|
||
return [self initWithRegionCode:regionCode bundle:[NSBundle mainBundle]];
|
||
}
|
||
|
||
- (instancetype)initWithRegionCode:(NSString *)regionCode bundle:(NSBundle *)bundle {
|
||
self = [self init];
|
||
if (self) {
|
||
/**
|
||
* @private
|
||
* @type {i18n.phonenumbers.PhoneNumberUtil}
|
||
*/
|
||
self.phoneUtil_ = [NBPhoneNumberUtil sharedInstance];
|
||
self.defaultCountry_ = regionCode;
|
||
self.currentMetaData_ = [self getMetadataForRegion_:self.defaultCountry_];
|
||
/**
|
||
* @type {i18n.phonenumbers.PhoneMetadata}
|
||
* @private
|
||
*/
|
||
self.defaultMetaData_ = self.currentMetaData_;
|
||
}
|
||
|
||
return self;
|
||
}
|
||
|
||
/**
|
||
* The metadata needed by this class is the same for all regions sharing the
|
||
* same country calling code. Therefore, we return the metadata for "main"
|
||
* region for this country calling code.
|
||
* - param regionCode regionCode an ISO 3166-1 two-letter region code.
|
||
* @return {i18n.phonenumbers.PhoneMetadata} main metadata for this region.
|
||
* @private
|
||
*/
|
||
- (NBPhoneMetaData *)getMetadataForRegion_:(NSString *)regionCode {
|
||
NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
|
||
/** @type {number} */
|
||
NSNumber *countryCallingCode = [self.phoneUtil_ getCountryCodeForRegion:regionCode];
|
||
/** @type {string} */
|
||
NSString *mainCountry = [self.phoneUtil_ getRegionCodeForCountryCode:countryCallingCode];
|
||
/** @type {i18n.phonenumbers.PhoneMetadata} */
|
||
NBPhoneMetaData *metadata = [helper getMetadataForRegion:mainCountry];
|
||
if (metadata != nil) {
|
||
return metadata;
|
||
}
|
||
// Set to a default instance of the metadata. This allows us to function with
|
||
// an incorrect region code, even if formatting only works for numbers
|
||
// specified with '+'.
|
||
return [[NBPhoneMetaData alloc] init];
|
||
}
|
||
|
||
/**
|
||
* @return {BOOL} YES if a new template is created as opposed to reusing the
|
||
* existing template.
|
||
* @private
|
||
*/
|
||
- (BOOL)maybeCreateNewTemplate_ {
|
||
// When there are multiple available formats, the formatter uses the first
|
||
// format where a formatting template could be created.
|
||
/** @type {number} */
|
||
NSUInteger possibleFormatsLength = [self.possibleFormats_ count];
|
||
for (NSUInteger i = 0; i < possibleFormatsLength; ++i) {
|
||
/** @type {i18n.phonenumbers.NumberFormat} */
|
||
NBNumberFormat *numberFormat =
|
||
[self.possibleFormats_ nb_safeObjectAtIndex:i class:[NBNumberFormat class]];
|
||
/** @type {string} */
|
||
NSString *pattern = numberFormat.pattern;
|
||
|
||
if (!pattern.length || [self.currentFormattingPattern_ isEqualToString:pattern]) {
|
||
return NO;
|
||
}
|
||
|
||
if ([self createFormattingTemplate_:numberFormat]) {
|
||
self.currentFormattingPattern_ = pattern;
|
||
NSRange nationalPrefixRange =
|
||
NSMakeRange(0, [numberFormat.nationalPrefixFormattingRule length]);
|
||
if (nationalPrefixRange.length > 0) {
|
||
NSTextCheckingResult *matchResult = [self.NATIONAL_PREFIX_SEPARATORS_PATTERN_
|
||
firstMatchInString:numberFormat.nationalPrefixFormattingRule
|
||
options:0
|
||
range:nationalPrefixRange];
|
||
self.shouldAddSpaceAfterNationalPrefix_ = (matchResult != nil);
|
||
} else {
|
||
self.shouldAddSpaceAfterNationalPrefix_ = NO;
|
||
}
|
||
// With a new formatting template, the matched position using the old
|
||
// template needs to be reset.
|
||
self.lastMatchPosition_ = 0;
|
||
return YES;
|
||
}
|
||
}
|
||
self.ableToFormat_ = NO;
|
||
return NO;
|
||
}
|
||
|
||
/**
|
||
* - param leadingDigits leadingThreeDigits first three digits of entered number.
|
||
* @private
|
||
*/
|
||
- (void)getAvailableFormats_:(NSString *)leadingDigits {
|
||
/** @type {Array.<i18n.phonenumbers.NumberFormat>} */
|
||
BOOL isIntlNumberFormats =
|
||
(self.isCompleteNumber_ && self.currentMetaData_.intlNumberFormats.count > 0);
|
||
NSArray *formatList = isIntlNumberFormats ? self.currentMetaData_.intlNumberFormats
|
||
: self.currentMetaData_.numberFormats;
|
||
|
||
/** @type {number} */
|
||
NSUInteger formatListLength = formatList.count;
|
||
|
||
for (NSUInteger i = 0; i < formatListLength; ++i) {
|
||
/** @type {i18n.phonenumbers.NumberFormat} */
|
||
NBNumberFormat *format = [formatList nb_safeObjectAtIndex:i class:[NBNumberFormat class]];
|
||
/** @type {BOOL} */
|
||
BOOL nationalPrefixIsUsedByCountry =
|
||
(self.currentMetaData_.nationalPrefix && self.currentMetaData_.nationalPrefix.length > 0);
|
||
|
||
if (!nationalPrefixIsUsedByCountry || self.isCompleteNumber_ ||
|
||
format.nationalPrefixOptionalWhenFormatting ||
|
||
[self.phoneUtil_ formattingRuleHasFirstGroupOnly:format.nationalPrefixFormattingRule]) {
|
||
if ([self isFormatEligible_:format.format]) {
|
||
[self.possibleFormats_ addObject:format];
|
||
}
|
||
}
|
||
}
|
||
|
||
[self narrowDownPossibleFormats_:leadingDigits];
|
||
}
|
||
|
||
/**
|
||
* - param format format
|
||
* @return {BOOL}
|
||
* @private
|
||
*/
|
||
- (BOOL)isFormatEligible_:(NSString *)format {
|
||
if (!format.length) {
|
||
return NO;
|
||
}
|
||
NSTextCheckingResult *matchResult =
|
||
[self.ELIGIBLE_FORMAT_PATTERN_ firstMatchInString:format
|
||
options:0
|
||
range:NSMakeRange(0, [format length])];
|
||
return (matchResult != nil);
|
||
}
|
||
|
||
/**
|
||
* - param leadingDigits leadingDigits
|
||
* @private
|
||
*/
|
||
- (void)narrowDownPossibleFormats_:(NSString *)leadingDigits {
|
||
/** @type {Array.<i18n.phonenumbers.NumberFormat>} */
|
||
NSMutableArray *possibleFormats = [[NSMutableArray alloc] init];
|
||
/** @type {number} */
|
||
NSUInteger indexOfLeadingDigitsPattern = leadingDigits.length - NBMinLeadingDigitsLength;
|
||
/** @type {number} */
|
||
NSUInteger possibleFormatsLength = self.possibleFormats_.count;
|
||
|
||
for (NSUInteger i = 0; i < possibleFormatsLength; ++i) {
|
||
/** @type {i18n.phonenumbers.NumberFormat} */
|
||
NBNumberFormat *format =
|
||
[self.possibleFormats_ nb_safeObjectAtIndex:i class:[NBNumberFormat class]];
|
||
|
||
if (format.leadingDigitsPatterns.count == 0) {
|
||
// Keep everything that isn't restricted by leading digits.
|
||
[possibleFormats addObject:format];
|
||
continue;
|
||
}
|
||
|
||
/** @type {number} */
|
||
NSInteger lastLeadingDigitsPattern =
|
||
MIN(indexOfLeadingDigitsPattern, format.leadingDigitsPatterns.count - 1);
|
||
|
||
/** @type {string} */
|
||
NSString *leadingDigitsPattern =
|
||
[format.leadingDigitsPatterns nb_safeStringAtIndex:lastLeadingDigitsPattern];
|
||
|
||
if ([self.phoneUtil_ stringPositionByRegex:leadingDigits regex:leadingDigitsPattern] == 0) {
|
||
[possibleFormats addObject:format];
|
||
}
|
||
}
|
||
self.possibleFormats_ = possibleFormats;
|
||
}
|
||
|
||
/**
|
||
* - param {i18n.phonenumbers.NumberFormat} format
|
||
* @return {BOOL}
|
||
* @private
|
||
*/
|
||
- (BOOL)createFormattingTemplate_:(NBNumberFormat *)format {
|
||
/** @type {string} */
|
||
NSString *numberPattern = format.pattern;
|
||
|
||
// The formatter doesn't format numbers when numberPattern contains '|', e.g.
|
||
// (20|3)\d{4}. In those cases we quickly return.
|
||
NSRange stringRange = [numberPattern rangeOfString:@"|"];
|
||
if (stringRange.location != NSNotFound) {
|
||
return NO;
|
||
}
|
||
|
||
// Replace anything in the form of [..] with \d
|
||
numberPattern = [self.CHARACTER_CLASS_PATTERN_
|
||
stringByReplacingMatchesInString:numberPattern
|
||
options:0
|
||
range:NSMakeRange(0, [numberPattern length])
|
||
withTemplate:@"\\\\d"];
|
||
|
||
// Replace any standalone digit (not the one in d{}) with \d
|
||
numberPattern = [self.STANDALONE_DIGIT_PATTERN_
|
||
stringByReplacingMatchesInString:numberPattern
|
||
options:0
|
||
range:NSMakeRange(0, [numberPattern length])
|
||
withTemplate:@"\\\\d"];
|
||
[self.formattingTemplate_ setString:@""];
|
||
|
||
/** @type {string} */
|
||
NSString *tempTemplate = [self getFormattingTemplate_:numberPattern numberFormat:format.format];
|
||
if (tempTemplate.length > 0) {
|
||
[self.formattingTemplate_ appendString:tempTemplate];
|
||
return YES;
|
||
}
|
||
return NO;
|
||
}
|
||
|
||
/**
|
||
* Gets a formatting template which can be used to efficiently format a
|
||
* partial number where digits are added one by one.
|
||
*
|
||
* - param {string} numberPattern
|
||
* - param {string} numberFormat
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)getFormattingTemplate_:(NSString *)numberPattern
|
||
numberFormat:(NSString *)numberFormat {
|
||
// Creates a phone number consisting only of the digit 9 that matches the
|
||
// numberPattern by applying the pattern to the longestPhoneNumber string.
|
||
/** @type {string} */
|
||
NSString *longestPhoneNumber = @"999999999999999";
|
||
|
||
/** @type {Array.<string>} */
|
||
NSArray *m = [self.phoneUtil_ matchedStringByRegex:longestPhoneNumber regex:numberPattern];
|
||
|
||
// this match will always succeed
|
||
/** @type {string} */
|
||
NSString *aPhoneNumber = [m nb_safeStringAtIndex:0];
|
||
// No formatting template can be created if the number of digits entered so
|
||
// far is longer than the maximum the current formatting rule can accommodate.
|
||
if (aPhoneNumber.length < self.nationalNumber_.length) {
|
||
return @"";
|
||
}
|
||
// Formats the number according to numberFormat
|
||
/** @type {string} */
|
||
NSString *template = [self.phoneUtil_ replaceStringByRegex:aPhoneNumber
|
||
regex:numberPattern
|
||
withTemplate:numberFormat];
|
||
|
||
// Replaces each digit with character DIGIT_PLACEHOLDER
|
||
template =
|
||
[self.phoneUtil_ replaceStringByRegex:template regex:@"9" withTemplate:NBDigitPlaceHolder];
|
||
return template;
|
||
}
|
||
|
||
/**
|
||
* Clears the internal state of the formatter, so it can be reused.
|
||
*/
|
||
- (void)clear {
|
||
self.currentOutput_ = @"";
|
||
[self.accruedInput_ setString:@""];
|
||
[self.accruedInputWithoutFormatting_ setString:@""];
|
||
[self.formattingTemplate_ setString:@""];
|
||
self.lastMatchPosition_ = 0;
|
||
self.currentFormattingPattern_ = @"";
|
||
[self.prefixBeforeNationalNumber_ setString:@""];
|
||
self.nationalPrefixExtracted_ = @"";
|
||
[self.nationalNumber_ setString:@""];
|
||
self.ableToFormat_ = YES;
|
||
self.inputHasFormatting_ = NO;
|
||
self.positionToRemember_ = 0;
|
||
self.originalPosition_ = 0;
|
||
self.isCompleteNumber_ = NO;
|
||
self.isExpectingCountryCallingCode_ = NO;
|
||
[self.possibleFormats_ removeAllObjects];
|
||
self.shouldAddSpaceAfterNationalPrefix_ = NO;
|
||
|
||
if (self.currentMetaData_ != self.defaultMetaData_) {
|
||
self.currentMetaData_ = [self getMetadataForRegion_:self.defaultCountry_];
|
||
}
|
||
}
|
||
|
||
- (NSString *)removeLastDigitAndRememberPosition {
|
||
NSString *accruedInputWithoutFormatting = [self.accruedInput_ copy];
|
||
[self clear];
|
||
|
||
NSString *result = @"";
|
||
|
||
NSUInteger length = accruedInputWithoutFormatting.length;
|
||
if (length == 0) {
|
||
return result;
|
||
}
|
||
|
||
for (NSUInteger i = 0; i < length - 1; i++) {
|
||
NSString *ch = [accruedInputWithoutFormatting substringWithRange:NSMakeRange(i, 1)];
|
||
result = [self inputDigitAndRememberPosition:ch];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
- (NSString *)removeLastDigit {
|
||
NSString *accruedInputWithoutFormatting = [self.accruedInput_ copy];
|
||
[self clear];
|
||
|
||
NSString *result = @"";
|
||
|
||
NSUInteger length = accruedInputWithoutFormatting.length;
|
||
if (length == 0) {
|
||
return result;
|
||
}
|
||
|
||
for (NSUInteger i = 0; i < length - 1; i++) {
|
||
NSString *ch = [accruedInputWithoutFormatting substringWithRange:NSMakeRange(i, 1)];
|
||
result = [self inputDigit:ch];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
- (NSString *)inputStringAndRememberPosition:(NSString *)string {
|
||
[self clear];
|
||
|
||
NSString *result = @"";
|
||
|
||
NSUInteger length = string.length;
|
||
for (NSUInteger i = 0; i < length; i++) {
|
||
NSString *ch = [string substringWithRange:NSMakeRange(i, 1)];
|
||
result = [self inputDigitAndRememberPosition:ch];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
- (NSString *)inputString:(NSString *)string {
|
||
[self clear];
|
||
|
||
NSString *result = @"";
|
||
|
||
NSUInteger length = string.length;
|
||
for (NSUInteger i = 0; i < length; i++) {
|
||
NSString *ch = [string substringWithRange:NSMakeRange(i, 1)];
|
||
result = [self inputDigit:ch];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Formats a phone number on-the-fly as each digit is entered.
|
||
*
|
||
* - param {string} nextChar the most recently entered digit of a phone number.
|
||
* Formatting characters are allowed, but as soon as they are encountered
|
||
* this method formats the number as entered and not 'as you type' anymore.
|
||
* Full width digits and Arabic-indic digits are allowed, and will be shown
|
||
* as they are.
|
||
* @return {string} the partially formatted phone number.
|
||
*/
|
||
- (NSString *)inputDigit:(NSString *)nextChar {
|
||
if (!nextChar || nextChar.length <= 0) {
|
||
return self.currentOutput_;
|
||
}
|
||
self.currentOutput_ = [self inputDigitWithOptionToRememberPosition_:nextChar rememberPosition:NO];
|
||
return self.currentOutput_;
|
||
}
|
||
|
||
/**
|
||
* Same as {@link #inputDigit}, but remembers the position where
|
||
* {@code nextChar} is inserted, so that it can be retrieved later by using
|
||
* {@link #getRememberedPosition}. The remembered position will be automatically
|
||
* adjusted if additional formatting characters are later inserted/removed in
|
||
* front of {@code nextChar}.
|
||
*
|
||
* - param {string} nextChar
|
||
* @return {string}
|
||
*/
|
||
- (NSString *)inputDigitAndRememberPosition:(NSString *)nextChar {
|
||
if (!nextChar || nextChar.length <= 0) {
|
||
return self.currentOutput_;
|
||
}
|
||
self.currentOutput_ =
|
||
[self inputDigitWithOptionToRememberPosition_:nextChar rememberPosition:YES];
|
||
return self.currentOutput_;
|
||
}
|
||
|
||
/**
|
||
* - param {string} nextChar
|
||
* - param {BOOL} rememberPosition
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
|
||
- (NSString *)inputDigitWithOptionToRememberPosition_:(NSString *)nextChar
|
||
rememberPosition:(BOOL)rememberPosition {
|
||
if (!nextChar || nextChar.length <= 0) {
|
||
_isSuccessfulFormatting = NO;
|
||
return self.currentOutput_;
|
||
}
|
||
|
||
[self.accruedInput_ appendString:nextChar];
|
||
|
||
if (rememberPosition) {
|
||
self.originalPosition_ = self.accruedInput_.length;
|
||
}
|
||
|
||
// We do formatting on-the-fly only when each character entered is either a
|
||
// digit, or a plus sign (accepted at the start of the number only).
|
||
if (![self isDigitOrLeadingPlusSign_:nextChar]) {
|
||
self.ableToFormat_ = NO;
|
||
self.inputHasFormatting_ = YES;
|
||
} else {
|
||
nextChar =
|
||
[self normalizeAndAccrueDigitsAndPlusSign_:nextChar rememberPosition:rememberPosition];
|
||
}
|
||
|
||
if (!self.ableToFormat_) {
|
||
// When we are unable to format because of reasons other than that
|
||
// formatting chars have been entered, it can be due to really long IDDs or
|
||
// NDDs. If that is the case, we might be able to do formatting again after
|
||
// extracting them.
|
||
|
||
if (self.inputHasFormatting_) {
|
||
_isSuccessfulFormatting = YES;
|
||
return [NSString stringWithString:self.accruedInput_];
|
||
} else if ([self attemptToExtractIdd_]) {
|
||
if ([self attemptToExtractCountryCallingCode_]) {
|
||
_isSuccessfulFormatting = YES;
|
||
return [self attemptToChoosePatternWithPrefixExtracted_];
|
||
}
|
||
} else if ([self ableToExtractLongerNdd_]) {
|
||
// Add an additional space to separate long NDD and national significant
|
||
// number for readability. We don't set shouldAddSpaceAfterNationalPrefix_
|
||
// to YES, since we don't want this to change later when we choose
|
||
// formatting templates.
|
||
[self.prefixBeforeNationalNumber_ appendString:NBSeparatorBeforeNationalNumber];
|
||
_isSuccessfulFormatting = YES;
|
||
return [self attemptToChoosePatternWithPrefixExtracted_];
|
||
}
|
||
|
||
_isSuccessfulFormatting = NO;
|
||
return self.accruedInput_;
|
||
}
|
||
|
||
// We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH
|
||
// digits (the plus sign is counted as a digit as well for this purpose) have
|
||
// been entered.
|
||
switch (self.accruedInputWithoutFormatting_.length) {
|
||
case 0:
|
||
case 1:
|
||
case 2:
|
||
_isSuccessfulFormatting = YES;
|
||
return self.accruedInput_;
|
||
case 3:
|
||
if ([self attemptToExtractIdd_]) {
|
||
self.isExpectingCountryCallingCode_ = YES;
|
||
} else {
|
||
// No IDD or plus sign is found, might be entering in national format.
|
||
self.nationalPrefixExtracted_ = [self removeNationalPrefixFromNationalNumber_];
|
||
_isSuccessfulFormatting = YES;
|
||
return [self attemptToChooseFormattingPattern_];
|
||
}
|
||
default:
|
||
if (self.isExpectingCountryCallingCode_) {
|
||
if ([self attemptToExtractCountryCallingCode_]) {
|
||
self.isExpectingCountryCallingCode_ = NO;
|
||
}
|
||
_isSuccessfulFormatting = YES;
|
||
return [NSString
|
||
stringWithFormat:@"%@%@", self.prefixBeforeNationalNumber_, self.nationalNumber_];
|
||
}
|
||
|
||
if (self.possibleFormats_.count > 0) {
|
||
// The formatting patterns are already chosen.
|
||
/** @type {string} */
|
||
NSString *tempNationalNumber = [self inputDigitHelper_:nextChar];
|
||
// See if the accrued digits can be formatted properly already. If not,
|
||
// use the results from inputDigitHelper, which does formatting based on
|
||
// the formatting pattern chosen.
|
||
/** @type {string} */
|
||
NSString *formattedNumber = [self attemptToFormatAccruedDigits_];
|
||
if (formattedNumber.length > 0) {
|
||
_isSuccessfulFormatting = YES;
|
||
return formattedNumber;
|
||
}
|
||
|
||
[self narrowDownPossibleFormats_:self.nationalNumber_];
|
||
|
||
if ([self maybeCreateNewTemplate_]) {
|
||
_isSuccessfulFormatting = YES;
|
||
return [self inputAccruedNationalNumber_];
|
||
}
|
||
|
||
if (self.ableToFormat_) {
|
||
_isSuccessfulFormatting = YES;
|
||
return [self appendNationalNumber_:tempNationalNumber];
|
||
} else {
|
||
_isSuccessfulFormatting = NO;
|
||
return self.accruedInput_;
|
||
}
|
||
} else {
|
||
_isSuccessfulFormatting = NO;
|
||
return [self attemptToChooseFormattingPattern_];
|
||
}
|
||
}
|
||
|
||
_isSuccessfulFormatting = NO;
|
||
}
|
||
|
||
/**
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)attemptToChoosePatternWithPrefixExtracted_ {
|
||
self.ableToFormat_ = YES;
|
||
self.isExpectingCountryCallingCode_ = NO;
|
||
[self.possibleFormats_ removeAllObjects];
|
||
return [self attemptToChooseFormattingPattern_];
|
||
}
|
||
|
||
/**
|
||
* Some national prefixes are a substring of others. If extracting the shorter
|
||
* NDD doesn't result in a number we can format, we try to see if we can extract
|
||
* a longer version here.
|
||
* @return {BOOL}
|
||
* @private
|
||
*/
|
||
- (BOOL)ableToExtractLongerNdd_ {
|
||
if (self.nationalPrefixExtracted_.length > 0) {
|
||
// Put the extracted NDD back to the national number before attempting to
|
||
// extract a new NDD.
|
||
/** @type {string} */
|
||
NSString *nationalNumberStr = [NSString stringWithString:self.nationalNumber_];
|
||
self.nationalNumber_ = [self.nationalPrefixExtracted_ mutableCopy];
|
||
[self.nationalNumber_ appendString:nationalNumberStr];
|
||
// Remove the previously extracted NDD from prefixBeforeNationalNumber. We
|
||
// cannot simply set it to empty string because people sometimes incorrectly
|
||
// enter national prefix after the country code, e.g. +44 (0)20-1234-5678.
|
||
/** @type {string} */
|
||
NSString *prefixBeforeNationalNumberStr = [self.prefixBeforeNationalNumber_ copy];
|
||
NSRange lastRange = [prefixBeforeNationalNumberStr rangeOfString:self.nationalPrefixExtracted_
|
||
options:NSBackwardsSearch];
|
||
/** @type {number} */
|
||
NSUInteger indexOfPreviousNdd = lastRange.location;
|
||
self.prefixBeforeNationalNumber_ = [[prefixBeforeNationalNumberStr
|
||
substringWithRange:NSMakeRange(0, indexOfPreviousNdd)] mutableCopy];
|
||
}
|
||
|
||
return self.nationalPrefixExtracted_ != [self removeNationalPrefixFromNationalNumber_];
|
||
}
|
||
|
||
/**
|
||
* - param {string} nextChar
|
||
* @return {BOOL}
|
||
* @private
|
||
*/
|
||
- (BOOL)isDigitOrLeadingPlusSign_:(NSString *)nextChar {
|
||
NSString *digitPattern = [NSString stringWithFormat:@"([%@])", NB_VALID_DIGITS_STRING];
|
||
NSString *plusPattern = [NSString stringWithFormat:@"[%@]+", NB_PLUS_CHARS];
|
||
|
||
BOOL isDigitPattern = [[self.phoneUtil_ matchesByRegex:nextChar regex:digitPattern] count] > 0;
|
||
BOOL isPlusPattern = [[self.phoneUtil_ matchesByRegex:nextChar regex:plusPattern] count] > 0;
|
||
|
||
return isDigitPattern || (self.accruedInput_.length == 1 && isPlusPattern);
|
||
}
|
||
|
||
/**
|
||
* Check to see if there is an exact pattern match for these digits. If so, we
|
||
* should use this instead of any other formatting template whose
|
||
* leadingDigitsPattern also matches the input.
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)attemptToFormatAccruedDigits_ {
|
||
/** @type {string} */
|
||
NSString *nationalNumber = [NSString stringWithString:self.nationalNumber_];
|
||
|
||
/** @type {number} */
|
||
NSUInteger possibleFormatsLength = self.possibleFormats_.count;
|
||
for (NSUInteger i = 0; i < possibleFormatsLength; ++i) {
|
||
/** @type {i18n.phonenumbers.NumberFormat} */
|
||
NBNumberFormat *numberFormat = self.possibleFormats_[i];
|
||
/** @type {string} */
|
||
NSString *pattern = numberFormat.pattern;
|
||
/** @type {RegExp} */
|
||
NSString *patternRegExp = [NSString stringWithFormat:@"^(?:%@)$", pattern];
|
||
BOOL isPatternRegExp =
|
||
[[self.phoneUtil_ matchesByRegex:nationalNumber regex:patternRegExp] count] > 0;
|
||
if (isPatternRegExp) {
|
||
if (numberFormat.nationalPrefixFormattingRule.length > 0) {
|
||
NSArray *matches = [self.NATIONAL_PREFIX_SEPARATORS_PATTERN_
|
||
matchesInString:numberFormat.nationalPrefixFormattingRule
|
||
options:0
|
||
range:NSMakeRange(0, numberFormat.nationalPrefixFormattingRule.length)];
|
||
self.shouldAddSpaceAfterNationalPrefix_ = [matches count] > 0;
|
||
} else {
|
||
self.shouldAddSpaceAfterNationalPrefix_ = NO;
|
||
}
|
||
|
||
/** @type {string} */
|
||
NSString *formattedNumber = [self.phoneUtil_ replaceStringByRegex:nationalNumber
|
||
regex:pattern
|
||
withTemplate:numberFormat.format];
|
||
return [self appendNationalNumber_:formattedNumber];
|
||
}
|
||
}
|
||
return @"";
|
||
}
|
||
|
||
/**
|
||
* Combines the national number with any prefix (IDD/+ and country code or
|
||
* national prefix) that was collected. A space will be inserted between them if
|
||
* the current formatting template indicates this to be suitable.
|
||
* - param {string} nationalNumber The number to be appended.
|
||
* @return {string} The combined number.
|
||
* @private
|
||
*/
|
||
- (NSString *)appendNationalNumber_:(NSString *)nationalNumber {
|
||
/** @type {number} */
|
||
NSUInteger prefixBeforeNationalNumberLength = self.prefixBeforeNationalNumber_.length;
|
||
unichar blank_char = [NBSeparatorBeforeNationalNumber characterAtIndex:0];
|
||
if (self.shouldAddSpaceAfterNationalPrefix_ && prefixBeforeNationalNumberLength > 0 &&
|
||
[self.prefixBeforeNationalNumber_ characterAtIndex:prefixBeforeNationalNumberLength - 1] !=
|
||
blank_char) {
|
||
// We want to add a space after the national prefix if the national prefix
|
||
// formatting rule indicates that this would normally be done, with the
|
||
// exception of the case where we already appended a space because the NDD
|
||
// was surprisingly long.
|
||
|
||
return [NSString stringWithFormat:@"%@%@%@", self.prefixBeforeNationalNumber_,
|
||
NBSeparatorBeforeNationalNumber, nationalNumber];
|
||
} else {
|
||
return [NSString stringWithFormat:@"%@%@", self.prefixBeforeNationalNumber_, nationalNumber];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns the current position in the partially formatted phone number of the
|
||
* character which was previously passed in as the parameter of
|
||
* {@link #inputDigitAndRememberPosition}.
|
||
*
|
||
* @return {number}
|
||
*/
|
||
- (NSInteger)getRememberedPosition {
|
||
if (!self.ableToFormat_) {
|
||
return self.originalPosition_;
|
||
}
|
||
/** @type {number} */
|
||
NSInteger accruedInputIndex = 0;
|
||
/** @type {number} */
|
||
NSInteger currentOutputIndex = 0;
|
||
/** @type {string} */
|
||
NSString *accruedInputWithoutFormatting = self.accruedInputWithoutFormatting_;
|
||
/** @type {string} */
|
||
NSString *currentOutput = self.currentOutput_;
|
||
|
||
while (accruedInputIndex < self.positionToRemember_ &&
|
||
currentOutputIndex < currentOutput.length) {
|
||
if ([accruedInputWithoutFormatting characterAtIndex:accruedInputIndex] ==
|
||
[currentOutput characterAtIndex:currentOutputIndex]) {
|
||
accruedInputIndex++;
|
||
}
|
||
currentOutputIndex++;
|
||
}
|
||
return currentOutputIndex;
|
||
}
|
||
|
||
/**
|
||
* Attempts to set the formatting template and returns a string which contains
|
||
* the formatted version of the digits entered so far.
|
||
*
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)attemptToChooseFormattingPattern_ {
|
||
/** @type {string} */
|
||
NSString *nationalNumber = [self.nationalNumber_ copy];
|
||
// We start to attempt to format only when as least MIN_LEADING_DIGITS_LENGTH
|
||
// digits of national number (excluding national prefix) have been entered.
|
||
if (nationalNumber.length >= NBMinLeadingDigitsLength) {
|
||
[self getAvailableFormats_:nationalNumber];
|
||
// See if the accrued digits can be formatted properly already.
|
||
NSString *formattedNumber = [self attemptToFormatAccruedDigits_];
|
||
if (formattedNumber.length > 0) {
|
||
return formattedNumber;
|
||
}
|
||
return [self maybeCreateNewTemplate_] ? [self inputAccruedNationalNumber_] : self.accruedInput_;
|
||
} else {
|
||
return [self appendNationalNumber_:nationalNumber];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Invokes inputDigitHelper on each digit of the national number accrued, and
|
||
* returns a formatted string in the end.
|
||
*
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)inputAccruedNationalNumber_ {
|
||
/** @type {string} */
|
||
NSString *nationalNumber = [self.nationalNumber_ copy];
|
||
/** @type {number} */
|
||
NSUInteger lengthOfNationalNumber = nationalNumber.length;
|
||
if (lengthOfNationalNumber > 0) {
|
||
/** @type {string} */
|
||
NSString *tempNationalNumber = @"";
|
||
for (NSUInteger i = 0; i < lengthOfNationalNumber; i++) {
|
||
tempNationalNumber = [self
|
||
inputDigitHelper_:[NSString stringWithFormat:@"%C", [nationalNumber characterAtIndex:i]]];
|
||
}
|
||
return self.ableToFormat_ ? [self appendNationalNumber_:tempNationalNumber]
|
||
: self.accruedInput_;
|
||
} else {
|
||
return self.prefixBeforeNationalNumber_;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @return {BOOL} YES if the current country is a NANPA country and the
|
||
* national number begins with the national prefix.
|
||
* @private
|
||
*/
|
||
- (BOOL)isNanpaNumberWithNationalPrefix_ {
|
||
// For NANPA numbers beginning with 1[2-9], treat the 1 as the national
|
||
// prefix. The reason is that national significant numbers in NANPA always
|
||
// start with [2-9] after the national prefix. Numbers beginning with 1[01]
|
||
// can only be short/emergency numbers, which don't need the national prefix.
|
||
if (![self.currentMetaData_.countryCode isEqual:@1]) {
|
||
return NO;
|
||
}
|
||
|
||
/** @type {string} */
|
||
NSString *nationalNumber = [self.nationalNumber_ copy];
|
||
return ([nationalNumber characterAtIndex:0] == '1') &&
|
||
([nationalNumber characterAtIndex:1] != '0') &&
|
||
([nationalNumber characterAtIndex:1] != '1');
|
||
}
|
||
|
||
/**
|
||
* Returns the national prefix extracted, or an empty string if it is not
|
||
* present.
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)removeNationalPrefixFromNationalNumber_ {
|
||
/** @type {string} */
|
||
NSString *nationalNumber = [self.nationalNumber_ copy];
|
||
/** @type {number} */
|
||
NSUInteger startOfNationalNumber = 0;
|
||
|
||
if ([self isNanpaNumberWithNationalPrefix_]) {
|
||
startOfNationalNumber = 1;
|
||
[self.prefixBeforeNationalNumber_ appendFormat:@"1%@", NBSeparatorBeforeNationalNumber];
|
||
self.isCompleteNumber_ = YES;
|
||
} else if (self.currentMetaData_.nationalPrefixForParsing != nil &&
|
||
self.currentMetaData_.nationalPrefixForParsing.length > 0) {
|
||
/** @type {RegExp} */
|
||
NSString *nationalPrefixForParsing =
|
||
[NSString stringWithFormat:@"^(?:%@)", self.currentMetaData_.nationalPrefixForParsing];
|
||
/** @type {Array.<string>} */
|
||
NSArray *m =
|
||
[self.phoneUtil_ matchedStringByRegex:nationalNumber regex:nationalPrefixForParsing];
|
||
NSString *firstString = [m nb_safeStringAtIndex:0];
|
||
if (m != nil && firstString != nil && firstString.length > 0) {
|
||
// When the national prefix is detected, we use international formatting
|
||
// rules instead of national ones, because national formatting rules could
|
||
// contain local formatting rules for numbers entered without area code.
|
||
self.isCompleteNumber_ = YES;
|
||
startOfNationalNumber = firstString.length;
|
||
[self.prefixBeforeNationalNumber_
|
||
appendString:[nationalNumber substringWithRange:NSMakeRange(0, startOfNationalNumber)]];
|
||
}
|
||
}
|
||
|
||
self.nationalNumber_ = [[nationalNumber substringFromIndex:startOfNationalNumber] mutableCopy];
|
||
return [nationalNumber substringWithRange:NSMakeRange(0, startOfNationalNumber)];
|
||
}
|
||
|
||
/**
|
||
* Extracts IDD and plus sign to prefixBeforeNationalNumber when they are
|
||
* available, and places the remaining input into nationalNumber.
|
||
*
|
||
* @return {BOOL} YES when accruedInputWithoutFormatting begins with the
|
||
* plus sign or valid IDD for defaultCountry.
|
||
* @private
|
||
*/
|
||
- (BOOL)attemptToExtractIdd_ {
|
||
/** @type {string} */
|
||
NSString *accruedInputWithoutFormatting = [self.accruedInputWithoutFormatting_ copy];
|
||
/** @type {RegExp} */
|
||
NSString *internationalPrefix =
|
||
[NSString stringWithFormat:@"^(?:\\+|%@)", self.currentMetaData_.internationalPrefix];
|
||
/** @type {Array.<string>} */
|
||
NSArray *m = [self.phoneUtil_ matchedStringByRegex:accruedInputWithoutFormatting
|
||
regex:internationalPrefix];
|
||
|
||
NSString *firstString = [m nb_safeStringAtIndex:0];
|
||
|
||
if (m != nil && firstString != nil && firstString.length > 0) {
|
||
self.isCompleteNumber_ = YES;
|
||
/** @type {number} */
|
||
NSUInteger startOfCountryCallingCode = firstString.length;
|
||
self.nationalNumber_ =
|
||
[[accruedInputWithoutFormatting substringFromIndex:startOfCountryCallingCode] mutableCopy];
|
||
self.prefixBeforeNationalNumber_ = [[accruedInputWithoutFormatting
|
||
substringWithRange:NSMakeRange(0, startOfCountryCallingCode)] mutableCopy];
|
||
|
||
if ([accruedInputWithoutFormatting characterAtIndex:0] != '+') {
|
||
[self.prefixBeforeNationalNumber_ appendString:NBSeparatorBeforeNationalNumber];
|
||
}
|
||
return YES;
|
||
}
|
||
return NO;
|
||
}
|
||
|
||
/**
|
||
* Extracts the country calling code from the beginning of nationalNumber to
|
||
* prefixBeforeNationalNumber when they are available, and places the remaining
|
||
* input into nationalNumber.
|
||
*
|
||
* @return {BOOL} YES when a valid country calling code can be found.
|
||
* @private
|
||
*/
|
||
- (BOOL)attemptToExtractCountryCallingCode_ {
|
||
if (self.nationalNumber_.length == 0) {
|
||
return NO;
|
||
}
|
||
|
||
/** @type {!goog.string.StringBuffer} */
|
||
NSString *numberWithoutCountryCallingCode = @"";
|
||
|
||
/** @type {number} */
|
||
NSNumber *countryCode = [self.phoneUtil_ extractCountryCode:self.nationalNumber_
|
||
nationalNumber:&numberWithoutCountryCallingCode];
|
||
|
||
if ([countryCode isEqualToNumber:@0]) {
|
||
return NO;
|
||
}
|
||
|
||
self.nationalNumber_ = [numberWithoutCountryCallingCode mutableCopy];
|
||
|
||
/** @type {string} */
|
||
NSString *newRegionCode = [self.phoneUtil_ getRegionCodeForCountryCode:countryCode];
|
||
|
||
if ([NB_REGION_CODE_FOR_NON_GEO_ENTITY isEqualToString:newRegionCode]) {
|
||
NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
|
||
self.currentMetaData_ = [helper getMetadataForNonGeographicalRegion:countryCode];
|
||
} else if (newRegionCode != self.defaultCountry_) {
|
||
self.currentMetaData_ = [self getMetadataForRegion_:newRegionCode];
|
||
}
|
||
|
||
/** @type {string} */
|
||
[self.prefixBeforeNationalNumber_
|
||
appendFormat:@"%@%@", countryCode, NBSeparatorBeforeNationalNumber];
|
||
return YES;
|
||
}
|
||
|
||
/**
|
||
* Accrues digits and the plus sign to accruedInputWithoutFormatting for later
|
||
* use. If nextChar contains a digit in non-ASCII format (e.g. the full-width
|
||
* version of digits), it is first normalized to the ASCII version. The return
|
||
* value is nextChar itself, or its normalized version, if nextChar is a digit
|
||
* in non-ASCII format. This method assumes its input is either a digit or the
|
||
* plus sign.
|
||
*
|
||
* - param {string} nextChar
|
||
* - param {BOOL} rememberPosition
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)normalizeAndAccrueDigitsAndPlusSign_:(NSString *)nextChar
|
||
rememberPosition:(BOOL)rememberPosition {
|
||
/** @type {string} */
|
||
NSString *normalizedChar;
|
||
|
||
if ([nextChar isEqualToString:@"+"]) {
|
||
normalizedChar = nextChar;
|
||
[self.accruedInputWithoutFormatting_ appendString:nextChar];
|
||
} else {
|
||
normalizedChar = [[self.phoneUtil_ DIGIT_MAPPINGS] objectForKey:nextChar];
|
||
if (!normalizedChar) return @"";
|
||
|
||
[self.accruedInputWithoutFormatting_ appendString:normalizedChar];
|
||
[self.nationalNumber_ appendString:normalizedChar];
|
||
}
|
||
|
||
if (rememberPosition) {
|
||
self.positionToRemember_ = self.accruedInputWithoutFormatting_.length;
|
||
}
|
||
|
||
return normalizedChar;
|
||
}
|
||
|
||
/**
|
||
* - param {string} nextChar
|
||
* @return {string}
|
||
* @private
|
||
*/
|
||
- (NSString *)inputDigitHelper_:(NSString *)nextChar {
|
||
/** @type {string} */
|
||
NSString *formattingTemplate = [self.formattingTemplate_ copy];
|
||
NSString *subedString = @"";
|
||
|
||
if (formattingTemplate.length > self.lastMatchPosition_) {
|
||
subedString = [formattingTemplate substringFromIndex:self.lastMatchPosition_];
|
||
}
|
||
|
||
if ([self.phoneUtil_ stringPositionByRegex:subedString regex:NBDigitPlaceHolder] >= 0) {
|
||
/** @type {number} */
|
||
int digitPatternStart =
|
||
[self.phoneUtil_ stringPositionByRegex:formattingTemplate regex:NBDigitPlaceHolder];
|
||
|
||
/** @type {string} */
|
||
NSRange tempRange = [formattingTemplate rangeOfString:NBDigitPlaceHolder];
|
||
NSString *tempTemplate =
|
||
[formattingTemplate stringByReplacingOccurrencesOfString:NBDigitPlaceHolder
|
||
withString:nextChar
|
||
options:NSLiteralSearch
|
||
range:tempRange];
|
||
self.formattingTemplate_ = [tempTemplate mutableCopy];
|
||
self.lastMatchPosition_ = digitPatternStart;
|
||
return [tempTemplate substringWithRange:NSMakeRange(0, self.lastMatchPosition_ + 1)];
|
||
} else {
|
||
if (self.possibleFormats_.count == 1) {
|
||
// More digits are entered than we could handle, and there are no other
|
||
// valid patterns to try.
|
||
self.ableToFormat_ = NO;
|
||
} // else, we just reset the formatting pattern.
|
||
self.currentFormattingPattern_ = @"";
|
||
return self.accruedInput_;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns the formatted number.
|
||
*
|
||
* @return {string}
|
||
*/
|
||
- (NSString *)description {
|
||
return self.currentOutput_;
|
||
}
|
||
|
||
@end
|