JS: Minor changes to types to make compiler happier.
[platform/upstream/libphonenumber.git] / javascript / i18n / phonenumbers / phonenumberutil.js
index ffc140b..264aecf 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @license
- * Copyright (C) 2010 The Libphonenumber Authors
+ * Copyright (C) 2010 The Libphonenumber Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,6 +33,8 @@ goog.provide('i18n.phonenumbers.Error');
 goog.provide('i18n.phonenumbers.PhoneNumberFormat');
 goog.provide('i18n.phonenumbers.PhoneNumberType');
 goog.provide('i18n.phonenumbers.PhoneNumberUtil');
+goog.provide('i18n.phonenumbers.PhoneNumberUtil.MatchType');
+goog.provide('i18n.phonenumbers.PhoneNumberUtil.ValidationResult');
 
 goog.require('goog.array');
 goog.require('goog.proto2.PbLiteSerializer');
@@ -101,7 +103,7 @@ i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_ = 1;
  * @type {number}
  * @private
  */
-i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 3;
+i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 2;
 
 
 /**
@@ -112,7 +114,7 @@ i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 3;
  * @type {number}
  * @private
  */
-i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_ = 16;
+i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_ = 17;
 
 
 /**
@@ -158,6 +160,22 @@ i18n.phonenumbers.PhoneNumberUtil.COLOMBIA_MOBILE_TO_FIXED_LINE_PREFIX_ = '3';
 
 
 /**
+ * Map of country calling codes that use a mobile token before the area code.
+ * One example of when this is relevant is when determining the length of the
+ * national destination code, which should be the length of the area code plus
+ * the length of the mobile token.
+ *
+ * @const
+ * @type {!Object.<number, string>}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.MOBILE_TOKEN_MAPPINGS_ = {
+  52: '1',
+  54: '9'
+};
+
+
+/**
  * The PLUS_SIGN signifies the international prefix.
  *
  * @const
@@ -193,14 +211,19 @@ i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_ = 'tel:';
 
 
 /**
- * We include the "+" here since RFC3966 format specifies that the context must
- * be specified in international format.
- *
  * @const
  * @type {string}
  * @private
  */
-i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context=+';
+i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context=';
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_ = ';isub=';
 
 
 /**
@@ -258,7 +281,7 @@ i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS = {
 /**
  * A map that contains characters that are essential when dialling. That means
  * any of the characters in this map must not be removed from a number when
- * dialing, otherwise the call will not reach the intended destination.
+ * dialling, otherwise the call will not reach the intended destination.
  *
  * @const
  * @type {!Object.<string, string>}
@@ -275,7 +298,7 @@ i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_ = {
   '7': '7',
   '8': '8',
   '9': '9',
-  '+': '+',
+  '+': i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN,
   '*': '*'
 };
 
@@ -514,8 +537,8 @@ i18n.phonenumbers.PhoneNumberUtil.UNIQUE_INTERNATIONAL_PREFIX_ =
  * @type {string}
  */
 i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION =
-    '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u200B\u2060\u3000()' +
-    '\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E';
+    '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u00AD\u200B\u2060\u3000' +
+    '()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E';
 
 
 /**
@@ -651,14 +674,36 @@ i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_PHONE_PATTERN_ =
  * used as a placeholder for carrier codes, for example in Brazilian phone
  * numbers. We also allow multiple '+' characters at the start.
  * Corresponds to the following:
+ * [digits]{minLengthNsn}|
  * plus_sign*
  * (([punctuation]|[star])*[digits]){3,}([punctuation]|[star]|[digits]|[alpha])*
+ *
+ * The first reg-ex is to allow short numbers (two digits long) to be parsed if
+ * they are entered as "15" etc, but only if there is no punctuation in them.
+ * The second expression restricts the number of digits to three or more, but
+ * then allows them to be in international form, and to have alpha-characters
+ * and punctuation. We split up the two reg-exes here and combine them when
+ * creating the reg-ex VALID_PHONE_NUMBER_PATTERN_ itself so we can prefix it
+ * with ^ and append $ to each branch.
+ *
  * Note VALID_PUNCTUATION starts with a -, so must be the first in the range.
  *
  * @const
  * @type {string}
  * @private
  */
+i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_PHONE_NUMBER_PATTERN_ =
+    '[' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']{' +
+    i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ + '}';
+
+
+/**
+ * See MIN_LENGTH_PHONE_NUMBER_PATTERN_ for a full description of this reg-exp.
+ *
+ * @const
+ * @type {string}
+ * @private
+ */
 i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ =
     '[' + i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ + ']*(?:[' +
     i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION +
@@ -748,10 +793,13 @@ i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERN_ =
  * @private
  */
 i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_PATTERN_ =
-    new RegExp('^' + i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ +
-               '(?:' +
-               i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERNS_FOR_PARSING_ +
-               ')?' + '$', 'i');
+    new RegExp(
+        '^' +
+        i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_PHONE_NUMBER_PATTERN_ +
+        '$|' +
+        '^' + i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ +
+        '(?:' + i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERNS_FOR_PARSING_ +
+        ')?' + '$', 'i');
 
 
 /**
@@ -799,6 +847,18 @@ i18n.phonenumbers.PhoneNumberUtil.CC_PATTERN_ = /\$CC/;
 
 
 /**
+ * A pattern that is used to determine if the national prefix formatting rule
+ * has the first group only, i.e., does not start with the national prefix.
+ * Note that the pattern explicitly allows for unbalanced parentheses.
+ * @const
+ * @type {!RegExp}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_ONLY_PREFIX_PATTERN_ =
+    /^\(?\$1\)?$/;
+
+
+/**
  * @const
  * @type {string}
  */
@@ -939,7 +999,7 @@ i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber = function(number) {
 
 /**
  * Checks to see if the string of characters could possibly be a phone number at
- * all. At the moment, checks to see that the string begins with at least 3
+ * all. At the moment, checks to see that the string begins with at least 2
  * digits, ignoring any punctuation commonly found in phone numbers. This method
  * does not require the number to be normalized in advance - but does assume
  * that leading non-number symbols have been removed, such as by the method
@@ -1082,30 +1142,19 @@ i18n.phonenumbers.PhoneNumberUtil.convertAlphaCharactersInNumber =
  */
 i18n.phonenumbers.PhoneNumberUtil.prototype.getLengthOfGeographicalAreaCode =
     function(number) {
-
-  if (number == null) {
-    return 0;
-  }
-  /** @type {?string} */
-  var regionCode = this.getRegionCodeForNumber(number);
-  if (!this.isValidRegionCode_(regionCode)) {
+  /** @type {i18n.phonenumbers.PhoneMetadata} */
+  var metadata = this.getMetadataForRegion(this.getRegionCodeForNumber(number));
+  if (metadata == null) {
     return 0;
   }
-  /** @type {i18n.phonenumbers.PhoneMetadata} */
-  var metadata = this.getMetadataForRegion(regionCode);
-  // If a country doesn't use a national prefix, and this number doesn't have an
-  // Italian leading zero, we assume it is a closed dialling plan with no area
-  // codes.
+  // If a country doesn't use a national prefix, and this number doesn't have
+  // an Italian leading zero, we assume it is a closed dialling plan with no
+  // area codes.
   if (!metadata.hasNationalPrefix() && !number.hasItalianLeadingZero()) {
     return 0;
   }
 
-  /** @type {i18n.phonenumbers.PhoneNumberType} */
-  var type = this.getNumberTypeHelper_(
-      this.getNationalSignificantNumber(number), metadata);
-  // Most numbers other than the two types below have to be dialled in full.
-  if (type != i18n.phonenumbers.PhoneNumberType.FIXED_LINE &&
-      type != i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE) {
+  if (!this.isNumberGeographical(number)) {
     return 0;
   }
 
@@ -1184,23 +1233,76 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getLengthOfNationalDestinationCode =
     return 0;
   }
 
-  if (this.getRegionCodeForCountryCode(number.getCountryCodeOrDefault()) ==
-      'AR' &&
-      this.getNumberType(number) == i18n.phonenumbers.PhoneNumberType.MOBILE) {
-    // Argentinian mobile numbers, when formatted in the international format,
-    // are in the form of +54 9 NDC XXXX.... As a result, we take the length of
-    // the third group (NDC) and add 1 for the digit 9, which also forms part of
-    // the national significant number.
-    //
-    // TODO: Investigate the possibility of better modeling the metadata to make
-    // it easier to obtain the NDC.
-    return numberGroups[2].length + 1;
+  if (this.getNumberType(number) == i18n.phonenumbers.PhoneNumberType.MOBILE) {
+    // For example Argentinian mobile numbers, when formatted in the
+    // international format, are in the form of +54 9 NDC XXXX.... As a result,
+    // we take the length of the third group (NDC) and add the length of the
+    // mobile token, which also forms part of the national significant number.
+    // This assumes that the mobile token is always formatted separately from
+    // the rest of the phone number.
+    /** @type {string} */
+    var mobileToken = i18n.phonenumbers.PhoneNumberUtil.getCountryMobileToken(
+        number.getCountryCodeOrDefault());
+    if (mobileToken != '') {
+      return numberGroups[2].length + mobileToken.length;
+    }
   }
   return numberGroups[1].length;
 };
 
 
 /**
+ * Returns the mobile token for the provided country calling code if it has
+ * one, otherwise returns an empty string. A mobile token is a number inserted
+ * before the area code when dialing a mobile number from that country from
+ * abroad.
+ *
+ * @param {number} countryCallingCode the country calling code for which we
+ *     want the mobile token.
+ * @return {string} the mobile token for the given country calling code.
+ */
+i18n.phonenumbers.PhoneNumberUtil.getCountryMobileToken =
+    function(countryCallingCode) {
+  return i18n.phonenumbers.PhoneNumberUtil.MOBILE_TOKEN_MAPPINGS_[
+      countryCallingCode] || '';
+};
+
+
+/**
+ * Convenience method to get a list of what regions the library has metadata
+ * for.
+ * @return {!Array.<string>} region codes supported by the library.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedRegions = function() {
+  return goog.array.filter(
+      Object.keys(i18n.phonenumbers.metadata.countryToMetadata),
+      function(regionCode) {
+        return isNaN(regionCode);
+      });
+};
+
+
+/**
+ * Convenience method to get a list of what global network calling codes the
+ * library has metadata for.
+ * @return {!Array.<number>} global network calling codes supported by the
+ *     library.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.
+    getSupportedGlobalNetworkCallingCodes = function() {
+  var callingCodesAsStrings = goog.array.filter(
+      Object.keys(i18n.phonenumbers.metadata.countryToMetadata),
+      function(regionCode) {
+        return !isNaN(regionCode);
+      });
+  return goog.array.map(callingCodesAsStrings,
+      function(callingCode) {
+        return parseInt(callingCode, 10);
+      });
+};
+
+
+/**
  * Normalizes a string of characters representing a phone number by replacing
  * all characters found in the accompanying map with the values therein, and
  * stripping all other characters if removeNonMatches is true.
@@ -1241,6 +1343,42 @@ i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_ =
 
 
 /**
+ * Helper function to check if the national prefix formatting rule has the first
+ * group only, i.e., does not start with the national prefix.
+ *
+ * @param {string} nationalPrefixFormattingRule The formatting rule for the
+ *     national prefix.
+ * @return {boolean} true if the national prefix formatting rule has the first
+ *     group only.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.formattingRuleHasFirstGroupOnly =
+    function(nationalPrefixFormattingRule) {
+  return nationalPrefixFormattingRule.length == 0 ||
+      i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_ONLY_PREFIX_PATTERN_.
+          test(nationalPrefixFormattingRule);
+};
+
+
+/**
+ * Tests whether a phone number has a geographical association. It checks if
+ * the number is associated to a certain region in the country where it belongs
+ * to. Note that this doesn't verify if the number is actually in use.
+ *
+ * @param {i18n.phonenumbers.PhoneNumber} phoneNumber The phone number to test.
+ * @return {boolean} true if the phone number has a geographical association.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.isNumberGeographical =
+    function(phoneNumber) {
+  /** @type {i18n.phonenumbers.PhoneNumberType} */
+  var numberType = this.getNumberType(phoneNumber);
+  // TODO: Include mobile phone numbers from countries like Indonesia, which
+  // has some mobile numbers that are geographical.
+  return numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE ||
+      numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE;
+};
+
+
+/**
  * Helper function to check region code is not unknown or null.
  *
  * @param {?string} regionCode the ISO 3166-1 two-letter region code.
@@ -1300,6 +1438,11 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.format =
     function(number, numberFormat) {
 
   if (number.getNationalNumber() == 0 && number.hasRawInput()) {
+    // Unparseable numbers that kept their raw input just use that.
+    // This is the only case where a number can be formatted as E164 without a
+    // leading '+' symbol (but the original number wasn't parseable anyway).
+    // TODO: Consider removing the 'if' above so that unparseable strings
+    // without raw input format to the empty string instead of "+00"
     /** @type {string} */
     var rawInput = number.getRawInputOrDefault();
     if (rawInput.length > 0) {
@@ -1311,22 +1454,26 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.format =
   /** @type {string} */
   var nationalSignificantNumber = this.getNationalSignificantNumber(number);
   if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.E164) {
-    // Early exit for E164 case since no formatting of the national number needs
-    // to be applied. Extensions are not formatted.
+    // Early exit for E164 case (even if the country calling code is invalid)
+    // since no formatting of the national number needs to be applied.
+    // Extensions are not formatted.
     return this.prefixNumberWithCountryCallingCode_(
         countryCallingCode, i18n.phonenumbers.PhoneNumberFormat.E164,
         nationalSignificantNumber, '');
   }
+  if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
+    return nationalSignificantNumber;
+  }
   // 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.
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
-  if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
-    return nationalSignificantNumber;
-  }
 
+  // Metadata cannot be null because the country calling code is valid (which
+  // means that the region code cannot be ZZ and must be one of our supported
+  // region codes).
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadata =
       this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
@@ -1366,15 +1513,16 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatByPattern =
   var countryCallingCode = number.getCountryCodeOrDefault();
   /** @type {string} */
   var nationalSignificantNumber = this.getNationalSignificantNumber(number);
+  if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
+    return nationalSignificantNumber;
+  }
   // 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.
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
-  if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
-    return nationalSignificantNumber;
-  }
+  // Metadata cannot be null because the country calling code is valid
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadata =
       this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
@@ -1392,13 +1540,13 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatByPattern =
     // 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.
-    /** type {i18n.phonenumbers.NumberFormat} */
+    /** @type {i18n.phonenumbers.NumberFormat} */
     var numFormatCopy = formattingPattern.clone();
     /** @type {string} */
     var nationalPrefixFormattingRule =
         formattingPattern.getNationalPrefixFormattingRuleOrDefault();
-    /** @type {string} */
     if (nationalPrefixFormattingRule.length > 0) {
+      /** @type {string} */
       var nationalPrefix = metadata.getNationalPrefixOrDefault();
       if (nationalPrefix.length > 0) {
         // Replace $NP with national prefix and $FG with the first group ($1).
@@ -1448,16 +1596,17 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
   var countryCallingCode = number.getCountryCodeOrDefault();
   /** @type {string} */
   var nationalSignificantNumber = this.getNationalSignificantNumber(number);
+  if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
+    return nationalSignificantNumber;
+  }
+
   // 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.
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
-  if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
-    return nationalSignificantNumber;
-  }
-
+  // Metadata cannot be null because the country calling code is valid.
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadata =
       this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
@@ -1543,52 +1692,101 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatNumberForMobileDialing =
   }
 
   /** @type {string} */
-  var formattedNumber;
+  var formattedNumber = '';
   // Clear the extension, as that part cannot normally be dialed together with
   // the main number.
   /** @type {i18n.phonenumbers.PhoneNumber} */
   var numberNoExt = number.clone();
   numberNoExt.clearExtension();
-  /** @type {i18n.phonenumbers.PhoneNumberType} */
-  var numberType = this.getNumberType(numberNoExt);
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
-  if (regionCode == 'CO' && regionCallingFrom == 'CO') {
-    if (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) {
+  /** @type {i18n.phonenumbers.PhoneNumberType} */
+  var numberType = this.getNumberType(numberNoExt);
+  /** @type {boolean} */
+  var isValidNumber = (numberType != i18n.phonenumbers.PhoneNumberType.UNKNOWN);
+  if (regionCallingFrom == regionCode) {
+    /** @type {boolean} */
+    var isFixedLineOrMobile =
+        (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) ||
+        (numberType == i18n.phonenumbers.PhoneNumberType.MOBILE) ||
+        (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE);
+    // Carrier codes may be needed in some countries. We handle this here.
+    if (regionCode == 'CO' &&
+        numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) {
       formattedNumber = this.formatNationalNumberWithCarrierCode(
           numberNoExt,
           i18n.phonenumbers.PhoneNumberUtil
               .COLOMBIA_MOBILE_TO_FIXED_LINE_PREFIX_);
+    } else if (regionCode == 'BR' && isFixedLineOrMobile) {
+      formattedNumber = numberNoExt.hasPreferredDomesticCarrierCode() ?
+          this.formatNationalNumberWithPreferredCarrierCode(numberNoExt, '') :
+          // Brazilian fixed line and mobile numbers need to be dialed with a
+          // carrier code when called within Brazil. Without that, most of the
+          // carriers won't connect the call. Because of that, we return an
+          // empty string here.
+          '';
+    } else if (isValidNumber && regionCode == 'HU') {
+      // The national format for HU numbers doesn't contain the national prefix,
+      // because that is how numbers are normally written down. However, the
+      // national prefix is obligatory when dialing from a mobile phone. As a
+      // result, we add it back here if it is a valid regular length phone
+      // number.
+      formattedNumber =
+          this.getNddPrefixForRegion(regionCode, true /* strip non-digits */) +
+          ' ' + this.format(numberNoExt,
+              i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+    } else if (countryCallingCode ==
+               i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_) {
+      // For NANPA countries, we output international format for numbers that
+      // can be dialed internationally, since that always works, except for
+      // numbers which might potentially be short numbers, which are always
+      // dialled in national format.
+      /** @type {i18n.phonenumbers.PhoneMetadata} */
+      var regionMetadata = this.getMetadataForRegion(regionCallingFrom);
+      if (this.canBeInternationallyDialled(numberNoExt) &&
+          !this.isShorterThanPossibleNormalNumber_(
+              regionMetadata, this.getNationalSignificantNumber(numberNoExt))) {
+        formattedNumber = this.format(
+            numberNoExt, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL);
+      } else {
+        formattedNumber = this.format(
+            numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+      }
     } else {
-      // E164 doesn't work at all when dialing within Colombia.
-      formattedNumber = this.format(
-          numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+      // For non-geographical countries, Mexican and Chilean fixed line and
+      // mobile numbers, we output international format for numbers that can be
+      // dialed internationally, as that always works.
+      if ((regionCode ==
+           i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY ||
+          // MX fixed line and mobile numbers should always be formatted in
+          // international format, even when dialed within MX. For national
+          // format to work, a carrier code needs to be used, and the correct
+          // carrier code depends on if the caller and callee are from the
+          // same local area. It is trickier to get that to work correctly than
+          // using international format, which is tested to work fine on all
+          // carriers.
+          // CL fixed line numbers need the national prefix when dialing in the
+          // national format, but don't have it when used for display. The
+          // reverse is true for mobile numbers. As a result, we output them in
+          // the international format to make it work.
+          ((regionCode == 'MX' || regionCode == 'CL') &&
+              isFixedLineOrMobile)) &&
+          this.canBeInternationallyDialled(numberNoExt)) {
+        formattedNumber = this.format(
+            numberNoExt, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL);
+      } else {
+        formattedNumber = this.format(
+            numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+      }
     }
-  } else if (regionCode == 'PE' && regionCallingFrom == 'PE') {
-    // In Peru, numbers cannot be dialled using E164 format from a mobile phone
-    // for Movistar. Instead they must be dialled in national format.
-    formattedNumber = this.format(
-        numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
-  } else if (regionCode == 'BR' && regionCallingFrom == 'BR' &&
-      ((numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) ||
-      (numberType == i18n.phonenumbers.PhoneNumberType.MOBILE) ||
-      (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE))) {
-    formattedNumber = numberNoExt.hasPreferredDomesticCarrierCode() ?
-        this.formatNationalNumberWithPreferredCarrierCode(numberNoExt, '') :
-        // Brazilian fixed line and mobile numbers need to be dialed with a
-        // carrier code when called within Brazil. Without that, most of the
-        // carriers won't connect the call. Because of that, we return an empty
-        // string here.
-        '';
-  } else if (this.canBeInternationallyDialled(numberNoExt)) {
+  } else if (isValidNumber && this.canBeInternationallyDialled(numberNoExt)) {
+    // We assume that short numbers are not diallable from outside their region,
+    // so if a number is not a valid regular length phone number, we treat it as
+    // if it cannot be internationally dialled.
     return withFormatting ?
         this.format(numberNoExt,
                     i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL) :
         this.format(numberNoExt, i18n.phonenumbers.PhoneNumberFormat.E164);
-  } else {
-    formattedNumber = (regionCallingFrom == regionCode) ?
-        this.format(numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL) :
-        '';
   }
   return withFormatting ?
       formattedNumber :
@@ -1642,9 +1840,9 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatOutOfCountryCallingNumber =
     }
   } else if (countryCallingCode ==
                  this.getCountryCodeForValidRegion_(regionCallingFrom)) {
-    // For regions that share a country calling code, the country calling code
-    // need not be dialled. This also applies when dialling within a region, so
-    // this if clause covers both these cases. Technically this is the case for
+    // If regions share a country calling code, the country calling code need
+    // not be dialled. This also applies when dialling within a region, so this
+    // if clause covers both these cases. Technically this is the case for
     // dialling from La Reunion to other overseas departments of France (French
     // Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover
     // this edge case for now and for those cases return the version including
@@ -1653,6 +1851,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatOutOfCountryCallingNumber =
     return this.format(number,
                        i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
   }
+  // Metadata cannot be null because we checked 'isValidRegionCode()' above.
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadataForRegionCallingFrom =
       this.getMetadataForRegion(regionCallingFrom);
@@ -1676,6 +1875,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatOutOfCountryCallingNumber =
 
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
+  // Metadata cannot be null because the country calling code is valid.
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadataForRegion =
       this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
@@ -1771,6 +1971,8 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatInOriginalFormat =
         formattedNumber = nationalFormat;
         break;
       }
+      // Metadata cannot be null here because getNddPrefixForRegion() (above)
+      // returns null if there is no metadata for the region.
       /** @type {i18n.phonenumbers.PhoneMetadata} */
       var metadata = this.getMetadataForRegion(regionCode);
       /** @type {string} */
@@ -1778,6 +1980,14 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatInOriginalFormat =
       /** @type {i18n.phonenumbers.NumberFormat} */
       var formatRule = this.chooseFormattingPatternForNumber_(
           metadata.numberFormatArray(), nationalNumber);
+      // The format rule could still be null here if the national number was 0
+      // and there was no raw input (this should not be possible for numbers
+      // generated by the phonenumber library as they would also not have a
+      // country calling code and we would have exited earlier).
+      if (formatRule == null) {
+        formattedNumber = nationalFormat;
+        break;
+      }
       // 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
@@ -1815,12 +2025,22 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatInOriginalFormat =
   // 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 &&
-          i18n.phonenumbers.PhoneNumberUtil
-              .normalizeDigitsOnly(formattedNumber) ==
-          i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly(rawInput)) ?
-      formattedNumber :
-      rawInput;
+  if (formattedNumber != null && rawInput.length > 0) {
+    /** @type {string} */
+    var normalizedFormattedNumber =
+        i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(formattedNumber,
+            i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_,
+            true /* remove non matches */);
+    /** @type {string} */
+    var normalizedRawInput =
+        i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(rawInput,
+            i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_,
+            true /* remove non matches */);
+    if (normalizedFormattedNumber != normalizedRawInput) {
+      formattedNumber = rawInput;
+    }
+  }
+  return formattedNumber;
 };
 
 
@@ -1969,7 +2189,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
     if (this.isNANPACountry(regionCallingFrom)) {
       return countryCode + ' ' + rawInput;
     }
-  } else if (this.isValidRegionCode_(regionCallingFrom) &&
+  } else if (metadataForRegionCallingFrom != null &&
       countryCode == this.getCountryCodeForValidRegion_(regionCallingFrom)) {
     /** @type {i18n.phonenumbers.NumberFormat} */
     var formattingPattern = this.chooseFormattingPatternForNumber_(
@@ -2013,6 +2233,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
   }
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCode);
+  // Metadata cannot be null because the country calling code is valid.
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadataForRegion =
       this.getMetadataForRegionOrCallingCode_(countryCode, regionCode);
@@ -2046,12 +2267,13 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
 i18n.phonenumbers.PhoneNumberUtil.prototype.getNationalSignificantNumber =
     function(number) {
 
-  // If a leading zero has been set, we prefix this now. Note this is not a
+  // If leading zero(s) have been set, we prefix this now. Note this is not a
   // national prefix.
   /** @type {string} */
   var nationalNumber = '' + number.getNationalNumber();
   if (number.hasItalianLeadingZero() && number.getItalianLeadingZero()) {
-    return '0' + nationalNumber;
+    return Array(number.getNumberOfLeadingZerosOrDefault() + 1).join('0') +
+        nationalNumber;
   }
   return nationalNumber;
 };
@@ -2167,7 +2389,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.chooseFormattingPatternForNumber_ =
 
 
 /**
- * Note that carrierCode is optional - if NULL or an empty string, no carrier
+ * Note that carrierCode is optional - if null or an empty string, no carrier
  * code replacement will take place.
  *
  * @param {string} nationalNumber a string of characters representing a phone
@@ -2403,16 +2625,14 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberType =
 
   /** @type {?string} */
   var regionCode = this.getRegionCodeForNumber(number);
-  if (!this.isValidRegionCode_(regionCode) &&
-      i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY !=
-      regionCode) {
+  /** @type {i18n.phonenumbers.PhoneMetadata} */
+  var metadata = this.getMetadataForRegionOrCallingCode_(
+      number.getCountryCodeOrDefault(), regionCode);
+  if (metadata == null) {
     return i18n.phonenumbers.PhoneNumberType.UNKNOWN;
   }
   /** @type {string} */
   var nationalSignificantNumber = this.getNationalSignificantNumber(number);
-  /** @type {i18n.phonenumbers.PhoneMetadata} */
-  var metadata = this.getMetadataForRegionOrCallingCode_(
-      number.getCountryCodeOrDefault(), regionCode);
   return this.getNumberTypeHelper_(nationalSignificantNumber, metadata);
 };
 
@@ -2426,10 +2646,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberType =
 i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberTypeHelper_ =
     function(nationalNumber, metadata) {
 
-  /** @type {i18n.phonenumbers.PhoneNumberDesc} */
-  var generalNumberDesc = metadata.getGeneralDesc();
-  if (!generalNumberDesc.hasNationalNumberPattern() ||
-      !this.isNumberMatchingDesc_(nationalNumber, generalNumberDesc)) {
+  if (!this.isNumberMatchingDesc_(nationalNumber, metadata.getGeneralDesc())) {
     return i18n.phonenumbers.PhoneNumberType.UNKNOWN;
   }
 
@@ -2482,6 +2699,9 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberTypeHelper_ =
 
 
 /**
+ * Returns the metadata for the given region code or {@code null} if the region
+ * code is invalid or unknown.
+ *
  * @param {?string} regionCode
  * @return {i18n.phonenumbers.PhoneMetadata}
  */
@@ -2564,6 +2784,10 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.isValidNumber = function(number) {
  * After this, the specific number pattern rules for the region are examined.
  * This is useful for determining for example whether a particular number is
  * valid for Canada, rather than just a valid NANPA number.
+ * Warning: In most cases, you want to use {@link #isValidNumber} instead. For
+ * example, this method will mark numbers from British Crown dependencies such
+ * as the Isle of Man as invalid for the region "GB" (United Kingdom), since it
+ * has its own region code, "IM", which may be undesirable.
  *
  * @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
  *     to validate.
@@ -2588,22 +2812,9 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.isValidNumberForRegion =
     // number does not match that of the region code.
     return false;
   }
-  /** @type {i18n.phonenumbers.PhoneNumberDesc} */
-  var generalNumDesc = metadata.getGeneralDesc();
   /** @type {string} */
   var nationalSignificantNumber = this.getNationalSignificantNumber(number);
 
-  // For regions where we don't have metadata for PhoneNumberDesc, we treat any
-  // number passed in as a valid number if its national significant number is
-  // between the minimum and maximum lengths defined by ITU for a national
-  // significant number.
-  if (!generalNumDesc.hasNationalNumberPattern()) {
-    /** @type {number} */
-    var numberLength = nationalSignificantNumber.length;
-    return numberLength >
-        i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ &&
-        numberLength <= i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_;
-  }
   return this.getNumberTypeHelper_(nationalSignificantNumber, metadata) !=
       i18n.phonenumbers.PhoneNumberType.UNKNOWN;
 };
@@ -2658,6 +2869,8 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
   for (var i = 0; i < regionCodesLength; i++) {
     regionCode = regionCodes[i];
     // If leadingDigits is present, use this. Otherwise, do full validation.
+    // Metadata cannot be null because the region codes come from the country
+    // calling code map.
     /** @type {i18n.phonenumbers.PhoneMetadata} */
     var metadata = this.getMetadataForRegion(regionCode);
     if (metadata.hasLeadingDigits()) {
@@ -2694,6 +2907,25 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getRegionCodeForCountryCode =
 
 
 /**
+ * Returns a list with the region codes that match the specific country calling
+ * code. For non-geographical country calling codes, the region code 001 is
+ * returned. Also, in the case of no region code being found, an empty list is
+ * returned.
+ *
+ * @param {number} countryCallingCode the country calling code.
+ * @return {Array.<string>}
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.getRegionCodesForCountryCode =
+    function(countryCallingCode) {
+
+  /** @type {Array.<string>} */
+  var regionCodes =
+      i18n.phonenumbers.metadata.countryCodeToRegionCodeMap[countryCallingCode];
+  return regionCodes == null ? [] : regionCodes;
+};
+
+
+/**
  * Returns the country calling code for a specific region. For example, this
  * would be 1 for the United States, and 64 for New Zealand.
  *
@@ -2721,6 +2953,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForRegion =
  *     calling code for.
  * @return {number} the country calling code for the region denoted by
  *     regionCode.
+ * @throws {string} if the region is invalid
  * @private
  */
 i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForValidRegion_ =
@@ -2728,6 +2961,9 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForValidRegion_ =
 
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadata = this.getMetadataForRegion(regionCode);
+  if (metadata == null) {
+    throw 'Invalid region code: ' + regionCode;
+  }
   return metadata.getCountryCodeOrDefault();
 };
 
@@ -2753,11 +2989,11 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForValidRegion_ =
  */
 i18n.phonenumbers.PhoneNumberUtil.prototype.getNddPrefixForRegion = function(
     regionCode, stripNonDigits) {
-  if (!this.isValidRegionCode_(regionCode)) {
-    return null;
-  }
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadata = this.getMetadataForRegion(regionCode);
+  if (metadata == null) {
+    return null;
+  }
   /** @type {string} */
   var nationalPrefix = metadata.getNationalPrefixOrDefault();
   // If no national prefix was found, we return null.
@@ -2802,7 +3038,8 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.isNANPACountry =
 i18n.phonenumbers.PhoneNumberUtil.prototype.isLeadingZeroPossible =
     function(countryCallingCode) {
   /** @type {i18n.phonenumbers.PhoneMetadata} */
-  var mainMetadataForCallingCode = this.getMetadataForRegion(
+  var mainMetadataForCallingCode = this.getMetadataForRegionOrCallingCode_(
+      countryCallingCode,
       this.getRegionCodeForCountryCode(countryCallingCode));
   return mainMetadataForCallingCode != null &&
       mainMetadataForCallingCode.getLeadingZeroPossibleOrDefault();
@@ -2877,6 +3114,25 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.testNumberLengthAgainstPattern_ =
 
 
 /**
+ * Helper method to check whether a number is too short to be a regular length
+ * phone number in a region.
+ *
+ * @param {i18n.phonenumbers.PhoneMetadata} regionMetadata
+ * @param {string} number
+ * @return {boolean}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.isShorterThanPossibleNormalNumber_ =
+    function(regionMetadata, number) {
+  /** @type {string} */
+  var possibleNumberPattern =
+      regionMetadata.getGeneralDesc().getPossibleNumberPatternOrDefault();
+  return this.testNumberLengthAgainstPattern_(possibleNumberPattern, number) ==
+      i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT;
+};
+
+
+/**
  * Check whether a phone number is a possible number. It provides a more lenient
  * check than {@link #isValidNumber} in the following sense:
  * <ol>
@@ -2919,27 +3175,13 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.isPossibleNumberWithReason =
   }
   /** @type {string} */
   var regionCode = this.getRegionCodeForCountryCode(countryCode);
+  // Metadata cannot be null because the country calling code is valid.
   /** @type {i18n.phonenumbers.PhoneMetadata} */
   var metadata =
       this.getMetadataForRegionOrCallingCode_(countryCode, regionCode);
-  /** @type {i18n.phonenumbers.PhoneNumberDesc} */
-  var generalNumDesc = metadata.getGeneralDesc();
-  // Handling case of numbers with no metadata.
-  if (!generalNumDesc.hasNationalNumberPattern()) {
-    /** @type {number} */
-    var numberLength = nationalNumber.length;
-    if (numberLength < i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) {
-      return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT;
-    } else if (numberLength >
-               i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_) {
-      return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_LONG;
-    } else {
-      return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE;
-    }
-  }
   /** @type {string} */
   var possibleNumberPattern =
-      generalNumDesc.getPossibleNumberPatternOrDefault();
+      metadata.getGeneralDesc().getPossibleNumberPatternOrDefault();
   return this.testNumberLengthAgainstPattern_(possibleNumberPattern,
                                               nationalNumber);
 };
@@ -3116,7 +3358,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.maybeExtractCountryCode =
   }
   if (countryCodeSource !=
       i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY) {
-    if (fullNumber.getLength() <
+    if (fullNumber.getLength() <=
         i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) {
       throw i18n.phonenumbers.Error.TOO_SHORT_AFTER_IDD;
     }
@@ -3284,7 +3526,6 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
 i18n.phonenumbers.PhoneNumberUtil.prototype.
     maybeStripNationalPrefixAndCarrierCode = function(number, metadata,
                                                       carrierCode) {
-
   /** @type {string} */
   var numberStr = number.toString();
   /** @type {number} */
@@ -3305,6 +3546,11 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
     /** @type {!RegExp} */
     var nationalNumberRule = new RegExp(
         metadata.getGeneralDesc().getNationalNumberPatternOrDefault());
+    // Check if the original number is viable.
+    /** @type {boolean} */
+    var isViableOriginalNumber =
+        i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
+            nationalNumberRule, numberStr);
     // prefixMatcher[numOfGroups] == null implies nothing was captured by the
     // capturing groups in possibleNationalPrefix; therefore, no transformation
     // is necessary, and we just remove the national prefix.
@@ -3312,34 +3558,43 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.
     var numOfGroups = prefixMatcher.length - 1;
     /** @type {?string} */
     var transformRule = metadata.getNationalPrefixTransformRule();
-    /** @type {string} */
-    var transformedNumber;
     /** @type {boolean} */
     var noTransform = transformRule == null || transformRule.length == 0 ||
                       prefixMatcher[numOfGroups] == null ||
                       prefixMatcher[numOfGroups].length == 0;
     if (noTransform) {
-      transformedNumber = numberStr.substring(prefixMatcher[0].length);
+      // If the original number was viable, and the resultant number is not,
+      // we return.
+      if (isViableOriginalNumber &&
+          !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
+              nationalNumberRule,
+              numberStr.substring(prefixMatcher[0].length))) {
+        return false;
+      }
+      if (carrierCode != null &&
+          numOfGroups > 0 && prefixMatcher[numOfGroups] != null) {
+        carrierCode.append(prefixMatcher[1]);
+      }
+      number.set(numberStr.substring(prefixMatcher[0].length));
+      return true;
     } else {
+      // Check that the resultant number is still viable. If not, return. Check
+      // this by copying the string buffer and making the transformation on the
+      // copy first.
+      /** @type {string} */
+      var transformedNumber;
       transformedNumber = numberStr.replace(prefixPattern, transformRule);
-    }
-    // If the original number was viable, and the resultant number is not,
-    // we return.
-    if (i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
-            nationalNumberRule, numberStr) &&
-        !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
-            nationalNumberRule, transformedNumber)) {
-      return false;
-    }
-    if ((noTransform && numOfGroups > 0 && prefixMatcher[1] != null) ||
-        (!noTransform && numOfGroups > 1)) {
-      if (carrierCode != null) {
+      if (isViableOriginalNumber &&
+          !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
+              nationalNumberRule, transformedNumber)) {
+        return false;
+      }
+      if (carrierCode != null && numOfGroups > 0) {
         carrierCode.append(prefixMatcher[1]);
       }
+      number.set(transformedNumber);
+      return true;
     }
-    number.clear();
-    number.append(transformedNumber);
-    return true;
   }
   return false;
 };
@@ -3416,7 +3671,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.checkRegionForParsing_ = function(
  *
  * @param {?string} numberToParse number that we are attempting to parse. This
  *     can contain formatting such as +, ( and -, as well as a phone number
- *     extension.
+ *     extension. It can also be provided in RFC3966 format.
  * @param {?string} defaultRegion region that we are expecting the number to be
  *     from. This is only used if the number being parsed is not written in
  *     international format. The country_code for the number in this case would
@@ -3466,6 +3721,33 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseAndKeepRawInput =
 
 
 /**
+ * A helper function to set the values related to leading zeros in a
+ * PhoneNumber.
+ *
+ * @param {string} nationalNumber the number we are parsing.
+ * @param {i18n.phonenumbers.PhoneNumber} phoneNumber a phone number proto
+ *     buffer to fill in.
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.setItalianLeadingZerosForPhoneNumber_ =
+    function(nationalNumber, phoneNumber) {
+  if (nationalNumber.length > 1 && nationalNumber.charAt(0) == '0') {
+    phoneNumber.setItalianLeadingZero(true);
+    var numberOfLeadingZeros = 1;
+    // Note that if the national number is all "0"s, the last "0" is not counted
+    // as a leading zero.
+    while (numberOfLeadingZeros < nationalNumber.length - 1 &&
+           nationalNumber.charAt(numberOfLeadingZeros) == '0') {
+      numberOfLeadingZeros++;
+    }
+    if (numberOfLeadingZeros != 1) {
+      phoneNumber.setNumberOfLeadingZeros(numberOfLeadingZeros);
+    }
+  }
+};
+
+
+/**
  * Parses a string and returns it in proto buffer format. This method is the
  * same as the public {@link #parse} method, with the exception that it allows
  * the default region to be null, for use by {@link #isNumberMatch}.
@@ -3493,35 +3775,12 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ =
     throw i18n.phonenumbers.Error.NOT_A_NUMBER;
   } else if (numberToParse.length >
       i18n.phonenumbers.PhoneNumberUtil.MAX_INPUT_STRING_LENGTH_) {
-    throw 'The string supplied was too long to parse';
+    throw i18n.phonenumbers.Error.TOO_LONG;
   }
 
-  /** @type {number} */
-  var indexOfPhoneContext = numberToParse.indexOf(
-      i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_);
   /** @type {!goog.string.StringBuffer} */
   var nationalNumber = new goog.string.StringBuffer();
-  if (indexOfPhoneContext > 0) {
-    // Prefix the number with the phone context. The offset here is because the
-    // context we are expecting to match should start with a "+" sign, and we
-    // want to include this at the start of the number.
-    nationalNumber.append(numberToParse.substring(
-        indexOfPhoneContext +
-        i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length - 1));
-    // Now append everything between the "tel:" prefix and the phone-context.
-    nationalNumber.append(numberToParse.substring(
-        numberToParse.indexOf(
-            i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_) +
-        i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length,
-        indexOfPhoneContext));
-    // Note that phone-contexts that are URLs will not be parsed -
-    // isViablePhoneNumber will throw an exception below.
-  } else {
-    // Extract a possible number from the string passed in (this strips leading
-    // characters that could not be the start of a phone number.)
-    nationalNumber.append(
-        i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber(numberToParse));
-  }
+  this.buildNationalNumberForParsing_(numberToParse, nationalNumber);
 
   if (!i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber(
       nationalNumber.toString())) {
@@ -3581,6 +3840,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ =
     /** @type {string} */
     var phoneNumberRegion = this.getRegionCodeForCountryCode(countryCode);
     if (phoneNumberRegion != defaultRegion) {
+      // Metadata cannot be null because the country calling code is valid.
       regionMetadata = this.getMetadataForRegionOrCallingCode_(
           countryCode, phoneNumberRegion);
     }
@@ -3603,12 +3863,19 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ =
   }
 
   if (regionMetadata != null) {
-    /** @type {goog.string.StringBuffer} */
+    /** @type {!goog.string.StringBuffer} */
     var carrierCode = new goog.string.StringBuffer();
+    /** @type {!goog.string.StringBuffer} */
+    var potentialNationalNumber =
+        new goog.string.StringBuffer(normalizedNationalNumber.toString());
     this.maybeStripNationalPrefixAndCarrierCode(
-        normalizedNationalNumber, regionMetadata, carrierCode);
-    if (keepRawInput) {
-      phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString());
+        potentialNationalNumber, regionMetadata, carrierCode);
+    if (!this.isShorterThanPossibleNormalNumber_(
+            regionMetadata, potentialNationalNumber.toString())) {
+      normalizedNationalNumber = potentialNationalNumber;
+      if (keepRawInput) {
+        phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString());
+      }
     }
   }
   /** @type {string} */
@@ -3623,15 +3890,89 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ =
       i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_) {
     throw i18n.phonenumbers.Error.TOO_LONG;
   }
-  if (normalizedNationalNumberStr.charAt(0) == '0') {
-    phoneNumber.setItalianLeadingZero(true);
-  }
+  this.setItalianLeadingZerosForPhoneNumber_(
+      normalizedNationalNumberStr, phoneNumber);
   phoneNumber.setNationalNumber(parseInt(normalizedNationalNumberStr, 10));
   return phoneNumber;
 };
 
 
 /**
+ * Converts numberToParse to a form that we can parse and write it to
+ * nationalNumber if it is written in RFC3966; otherwise extract a possible
+ * number out of it and write to nationalNumber.
+ *
+ * @param {?string} numberToParse number that we are attempting to parse. This
+ *     can contain formatting such as +, ( and -, as well as a phone number
+ *     extension.
+ * @param {!goog.string.StringBuffer} nationalNumber a string buffer for storing
+ *     the national significant number.
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.buildNationalNumberForParsing_ =
+    function(numberToParse, nationalNumber) {
+
+  /** @type {number} */
+  var indexOfPhoneContext = numberToParse.indexOf(
+      i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_);
+  if (indexOfPhoneContext > 0) {
+    var phoneContextStart = indexOfPhoneContext +
+        i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length;
+    // If the phone context contains a phone number prefix, we need to capture
+    // it, whereas domains will be ignored.
+    if (numberToParse.charAt(phoneContextStart) ==
+        i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) {
+      // Additional parameters might follow the phone context. If so, we will
+      // remove them here because the parameters after phone context are not
+      // important for parsing the phone number.
+      var phoneContextEnd = numberToParse.indexOf(';', phoneContextStart);
+      if (phoneContextEnd > 0) {
+        nationalNumber.append(numberToParse.substring(phoneContextStart,
+            phoneContextEnd));
+      } else {
+        nationalNumber.append(numberToParse.substring(phoneContextStart));
+      }
+    }
+
+    // Now append everything between the "tel:" prefix and the phone-context.
+    // This should include the national number, an optional extension or
+    // isdn-subaddress component. Note we also handle the case when "tel:" is
+    // missing, as we have seen in some of the phone number inputs.
+    // In that case, we append everything from the beginning.
+    var indexOfRfc3966Prefix = numberToParse.indexOf(
+        i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_);
+    var indexOfNationalNumber = (indexOfRfc3966Prefix >= 0) ?
+        indexOfRfc3966Prefix +
+        i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length : 0;
+    nationalNumber.append(numberToParse.substring(indexOfNationalNumber,
+        indexOfPhoneContext));
+  } else {
+    // Extract a possible number from the string passed in (this strips leading
+    // characters that could not be the start of a phone number.)
+    nationalNumber.append(
+        i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber(numberToParse));
+  }
+
+  // Delete the isdn-subaddress and everything after it if it is present.
+  // Note extension won't appear at the same time with isdn-subaddress
+  // according to paragraph 5.3 of the RFC3966 spec,
+  /** @type {string} */
+  var nationalNumberStr = nationalNumber.toString();
+  var indexOfIsdn = nationalNumberStr.indexOf(
+      i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_);
+  if (indexOfIsdn > 0) {
+    nationalNumber.clear();
+    nationalNumber.append(nationalNumberStr.substring(0, indexOfIsdn));
+  }
+  // If both phone context and isdn-subaddress are absent but other
+  // parameters are present, the parameters are left in nationalNumber. This
+  // is because we are concerned about deleting content from a potential
+  // number string when there is no strong evidence that the number is
+  // actually written in RFC3966.
+};
+
+
+/**
  * Takes two phone numbers and compares them for equality.
  *
  * <p>Returns EXACT_MATCH if the country_code, NSN, presence of a leading zero
@@ -3819,15 +4160,13 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.isNationalNumberSuffixOfTheOther_ =
  */
 i18n.phonenumbers.PhoneNumberUtil.prototype.canBeInternationallyDialled =
     function(number) {
-  /** @type {?string} */
-  var regionCode = this.getRegionCodeForNumber(number);
-  if (!this.isValidRegionCode_(regionCode)) {
+  /** @type {i18n.phonenumbers.PhoneMetadata} */
+  var metadata = this.getMetadataForRegion(this.getRegionCodeForNumber(number));
+  if (metadata == null) {
     // Note numbers belonging to non-geographical entities (e.g. +800 numbers)
     // are always internationally diallable, and will be caught here.
     return true;
   }
-  /** @type {i18n.phonenumbers.PhoneMetadata} */
-  var metadata = this.getMetadataForRegion(regionCode);
   /** @type {string} */
   var nationalSignificantNumber = this.getNationalSignificantNumber(number);
   return !this.isNumberMatchingDesc_(nationalSignificantNumber,