Adding inputDigitAndRememberPosition and getRememberedPosition methods to
AsYouTypeFormatter.java to provide better cursor control.

Change-Id: I26464c3cff3ce2550863a63ff23eb1bfc57898d3
diff --git a/README.android b/README.android
index 1ca189a..d137bba 100644
--- a/README.android
+++ b/README.android
@@ -1,4 +1,4 @@
 URL: http://code.google.com/p/libphonenumber/
-Version: r21
+Version: r28
 License: Apache 2
 Description: Google Phone Number Library.
diff --git a/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java
index e4e26d7..8936d5b 100644
--- a/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java
+++ b/java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java
@@ -54,11 +54,16 @@
   private String digitPlaceholder = "\u2008";
   private Pattern digitPattern = Pattern.compile(digitPlaceholder);
   private int lastMatchPosition = 0;
+  private boolean rememberPosition = false;
+  private int positionRemembered = 0;
+  private int originalPosition = 0;
   private Pattern nationalPrefixForParsing;
   private Pattern internationalPrefix;
   private StringBuffer prefixBeforeNationalNumber;
   private StringBuffer nationalNumber;
-  private final Pattern UNSUPPORTED_SYNTAX = Pattern.compile("[*#;,a-zA-Z]");
+  // No formatting will be applied when any of the character in the following character class is
+  // entered by users.
+  private final Pattern UNSUPPORTED_SYNTAX = Pattern.compile("[- *#;,.()/a-zA-Z]");
   private final Pattern CHARACTER_CLASS_PATTERN = Pattern.compile("\\[([^\\[\\]])*\\]");
   private final Pattern STANDALONE_DIGIT_PATTERN = Pattern.compile("\\d(?=[^,}][^,}])");
 
@@ -169,6 +174,8 @@
     prefixBeforeNationalNumber.setLength(0);
     nationalNumber.setLength(0);
     ableToFormat = true;
+    positionRemembered = 0;
+    originalPosition = 0;
     isInternationalFormatting = false;
     if (!currentMetaData.equals(defaultMetaData)) {
       initializeCountrySpecificInfo(defaultCountry);
@@ -185,11 +192,15 @@
    */
   public String inputDigit(char nextChar) {
     accruedInput.append(nextChar);
-    // * and # are normally used in mobile codes, which we do not format.
+    rememberPosition();
     if (UNSUPPORTED_SYNTAX.matcher(Character.toString(nextChar)).matches()) {
       ableToFormat = false;
     }
     if (!ableToFormat) {
+      if (positionRemembered > 0 && currentOutput.length() > 0) {
+        positionRemembered = originalPosition;
+        currentOutput.setLength(0);
+      }
       return accruedInput.toString();
     }
 
@@ -214,13 +225,43 @@
         return attemptToChooseFormattingPattern();
       default:
         if (nationalNumber.length() > 4) {  // The formatting pattern is already chosen.
-          return prefixBeforeNationalNumber + inputDigitHelper(nextChar);
+          String temp = inputDigitHelper(nextChar);
+          return ableToFormat
+              ? prefixBeforeNationalNumber + temp
+              : temp;
         } else {
           return attemptToChooseFormattingPattern();
         }
     }
   }
 
+  private void rememberPosition() {
+    if (rememberPosition) {
+      positionRemembered = accruedInput.length();
+      originalPosition = positionRemembered;
+    }
+  }
+
+  /**
+   * Same as inputDigit, but remembers the position where nextChar is inserted, so that it could be
+   * retrieved later by using getRememberedPosition(). The remembered position will be automatically
+   * adjusted if additional formatting characters are later inserted/removed in front of nextChar.
+   */
+  public String inputDigitAndRememberPosition(char nextChar) {
+    rememberPosition = true;
+    String result = inputDigit(nextChar);
+    rememberPosition = false;
+    return result;
+  }
+
+  /**
+   * Returns the current position in the partially formatted phone number of the character which was
+   * previously passed in as the parameter of inputDigitAndRememberPosition().
+   */
+  public int getRememberedPosition() {
+    return positionRemembered;
+  }
+
   // Attempts to set the formatting template and returns a string which contains the formatted
   // version of the digits entered so far.
   private String attemptToChooseFormattingPattern() {
@@ -230,6 +271,9 @@
       chooseFormatAndCreateTemplate(nationalNumber.substring(0, 4));
       return inputAccruedNationalNumber();
     } else {
+      if (rememberPosition) {
+        positionRemembered = prefixBeforeNationalNumber.length() + nationalNumber.length();
+      }
       return prefixBeforeNationalNumber + nationalNumber.toString();
     }
   }
@@ -239,12 +283,24 @@
   private String inputAccruedNationalNumber() {
     int lengthOfNationalNumber = nationalNumber.length();
     if (lengthOfNationalNumber > 0) {
+      // The positionRemembered should be only adjusted once in the loop that follows.
+      Boolean positionAlreadyAdjusted = false;
       for (int i = 0; i < lengthOfNationalNumber - 1; i++) {
-        inputDigitHelper(nationalNumber.charAt(i));
+        String temp = inputDigitHelper(nationalNumber.charAt(i));
+        if (!positionAlreadyAdjusted &&
+            positionRemembered - prefixBeforeNationalNumber.length() == i + 1) {
+          positionRemembered = prefixBeforeNationalNumber.length() + temp.length();
+          positionAlreadyAdjusted = true;
+        }
       }
-      return prefixBeforeNationalNumber
-             + inputDigitHelper(nationalNumber.charAt(lengthOfNationalNumber - 1));
+      String temp = inputDigitHelper(nationalNumber.charAt(lengthOfNationalNumber - 1));
+      return ableToFormat
+          ? prefixBeforeNationalNumber + temp
+          : temp;
     } else {
+      if (rememberPosition) {
+        positionRemembered = prefixBeforeNationalNumber.length();
+      }
       return prefixBeforeNationalNumber.toString();
     }
   }
@@ -253,7 +309,13 @@
     int startOfNationalNumber = 0;
     if (currentMetaData.getCountryCode() == 1 && nationalNumber.charAt(0) == '1') {
       startOfNationalNumber = 1;
-      prefixBeforeNationalNumber.append("1 ");
+      prefixBeforeNationalNumber.append("1");
+      // Since a space will be inserted after the national prefix in this case, we increase the
+      // remembered position by 1 for anything that is after the national prefix.
+      if (positionRemembered > prefixBeforeNationalNumber.length()) {
+        positionRemembered++;
+      }
+      prefixBeforeNationalNumber.append(" ");
     } else if (currentMetaData.hasNationalPrefix()) {
       Matcher m = nationalPrefixForParsing.matcher(nationalNumber);
       if (m.lookingAt()) {
@@ -291,9 +353,20 @@
         prefixBeforeNationalNumber.append(
             accruedInputWithoutFormatting.substring(0, startOfCountryCode));
         if (accruedInputWithoutFormatting.charAt(0) != PhoneNumberUtil.PLUS_SIGN ) {
+          if (positionRemembered > prefixBeforeNationalNumber.length()) {
+            // Since a space will be inserted in front of the country code in this case, we increase
+            // the remembered position by 1.
+            positionRemembered++;
+          }
           prefixBeforeNationalNumber.append(" ");
         }
-        prefixBeforeNationalNumber.append(countryCode).append(" ");
+        String countryCodeString = Integer.toString(countryCode);
+        if (positionRemembered > prefixBeforeNationalNumber.length() + countryCodeString.length()) {
+          // Since a space will be inserted after the country code in this case, we increase the
+          // remembered position by 1.
+          positionRemembered++;
+        }
+        prefixBeforeNationalNumber.append(countryCodeString).append(" ");
       }
     } else {
       nationalNumber.setLength(0);
@@ -328,11 +401,18 @@
     if (digitMatcher.find(lastMatchPosition)) {
       currentOutput = new StringBuffer(digitMatcher.replaceFirst(Character.toString(nextChar)));
       lastMatchPosition = digitMatcher.start();
+      if (rememberPosition) {
+        positionRemembered = prefixBeforeNationalNumber.length() + lastMatchPosition + 1;
+      }
       return currentOutput.substring(0, lastMatchPosition + 1);
     } else {  // More digits are entered than we could handle.
       currentOutput.append(nextChar);
       ableToFormat = false;
-      return currentOutput.toString();
+      if (positionRemembered > 0) {
+        positionRemembered = originalPosition;
+        currentOutput.setLength(0);
+      }
+      return accruedInput.toString();
     }
   }
 }
diff --git a/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java b/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java
index 076b4b4..7be5f5b 100644
--- a/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java
+++ b/java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java
@@ -67,43 +67,117 @@
     assertEquals("1 650 253 2222", formatter.inputDigit('2'));
 
     formatter.clear();
+    assertEquals("1", formatter.inputDigitAndRememberPosition('1'));
+    assertEquals(1, formatter.getRememberedPosition());
+    assertEquals("16", formatter.inputDigit('6'));
+    assertEquals("165", formatter.inputDigit('5'));
+    assertEquals(1, formatter.getRememberedPosition());
+    assertEquals("1650", formatter.inputDigitAndRememberPosition('0'));
+    assertEquals(4, formatter.getRememberedPosition());
+    assertEquals("16502", formatter.inputDigit('2'));
+    assertEquals("1 650 25", formatter.inputDigit('5'));
+    assertEquals(5, formatter.getRememberedPosition());
+    assertEquals("1 650 253", formatter.inputDigit('3'));
+    assertEquals("1 650 253 2", formatter.inputDigit('2'));
+    assertEquals("1 650 253 22", formatter.inputDigit('2'));
+    assertEquals(5, formatter.getRememberedPosition());
+    assertEquals("1 650 253 222", formatter.inputDigitAndRememberPosition('2'));
+    assertEquals(13, formatter.getRememberedPosition());
+    assertEquals("1 650 253 2222", formatter.inputDigit('2'));
+    assertEquals(13, formatter.getRememberedPosition());
+    assertEquals("165025322222", formatter.inputDigit('2'));
+    assertEquals(10, formatter.getRememberedPosition());
+    assertEquals("1650253222222", formatter.inputDigit('2'));
+    assertEquals(10, formatter.getRememberedPosition());
+
+    formatter.clear();
+    assertEquals("1", formatter.inputDigit('1'));
+    assertEquals("16", formatter.inputDigit('6'));
+    assertEquals("165", formatter.inputDigit('5'));
+    assertEquals("1650", formatter.inputDigitAndRememberPosition('0'));
+    assertEquals(4, formatter.getRememberedPosition());
+    assertEquals("16502", formatter.inputDigit('2'));
+    assertEquals("1 650 25", formatter.inputDigit('5'));
+    assertEquals(5, formatter.getRememberedPosition());
+    assertEquals("1 650 253", formatter.inputDigit('3'));
+    assertEquals("1 650 253 2", formatter.inputDigit('2'));
+    assertEquals("1 650 253 22", formatter.inputDigit('2'));
+    assertEquals(5, formatter.getRememberedPosition());
+    assertEquals("1 650 253 222", formatter.inputDigit('2'));
+    assertEquals("1 650 253 2222", formatter.inputDigit('2'));
+    assertEquals("165025322222", formatter.inputDigit('2'));
+    assertEquals(4, formatter.getRememberedPosition());
+    assertEquals("1650253222222", formatter.inputDigit('2'));
+    assertEquals(4, formatter.getRememberedPosition());    
+
+    formatter.clear();
+    assertEquals("1", formatter.inputDigit('1'));
+    assertEquals("16", formatter.inputDigit('6'));
+    assertEquals("165", formatter.inputDigitAndRememberPosition('5'));
+    assertEquals("1650", formatter.inputDigit('0'));
+    assertEquals(3, formatter.getRememberedPosition());
+    assertEquals("16502", formatter.inputDigit('2'));
+    assertEquals("1 650 25", formatter.inputDigit('5'));
+    assertEquals(4, formatter.getRememberedPosition());
+    assertEquals("1 650 253", formatter.inputDigit('3'));
+    assertEquals("1 650 253 2", formatter.inputDigit('2'));
+    assertEquals("1 650 253 22", formatter.inputDigit('2'));
+    assertEquals(4, formatter.getRememberedPosition());
+    assertEquals("1 650 253 222", formatter.inputDigit('2'));
+    assertEquals("1 650 253 2222", formatter.inputDigit('2'));
+    assertEquals("165025322222", formatter.inputDigit('2'));
+    assertEquals(3, formatter.getRememberedPosition());
+    assertEquals("1650253222222", formatter.inputDigit('2'));
+    assertEquals(3, formatter.getRememberedPosition());
+
+    formatter.clear();
     assertEquals("6", formatter.inputDigit('6'));
     assertEquals("65", formatter.inputDigit('5'));
     assertEquals("650", formatter.inputDigit('0'));
     assertEquals("6502", formatter.inputDigit('2'));
-    assertEquals("65025", formatter.inputDigit('5'));
+    assertEquals("65025", formatter.inputDigitAndRememberPosition('5'));
+    assertEquals(5, formatter.getRememberedPosition());
     assertEquals("650 253", formatter.inputDigit('3'));
+    assertEquals(6, formatter.getRememberedPosition());
     assertEquals("650 253 2", formatter.inputDigit('2'));
     assertEquals("650 253 22", formatter.inputDigit('2'));
     assertEquals("650 253 222", formatter.inputDigit('2'));
     // No more formatting when semicolon is entered.
     assertEquals("650253222;", formatter.inputDigit(';'));
+    assertEquals(5, formatter.getRememberedPosition());
     assertEquals("650253222;2", formatter.inputDigit('2'));
 
     formatter.clear();
     assertEquals("6", formatter.inputDigit('6'));
     assertEquals("65", formatter.inputDigit('5'));
     assertEquals("650", formatter.inputDigit('0'));
+    // No more formatting when users choose to do their own formatting.
     assertEquals("650-", formatter.inputDigit('-'));
-    assertEquals("650-2", formatter.inputDigit('2'));
+    assertEquals("650-2", formatter.inputDigitAndRememberPosition('2'));
+    assertEquals(5, formatter.getRememberedPosition());
     assertEquals("650-25", formatter.inputDigit('5'));
-    assertEquals("650 253", formatter.inputDigit('3'));
-    assertEquals("650 253", formatter.inputDigit('-'));
-    assertEquals("650 253 2", formatter.inputDigit('2'));
-    assertEquals("650 253 22", formatter.inputDigit('2'));
-    assertEquals("650 253 222", formatter.inputDigit('2'));
-    assertEquals("650 253 2222", formatter.inputDigit('2'));
+    assertEquals(5, formatter.getRememberedPosition());
+    assertEquals("650-253", formatter.inputDigit('3'));
+    assertEquals(5, formatter.getRememberedPosition());
+    assertEquals("650-253-", formatter.inputDigit('-'));
+    assertEquals("650-253-2", formatter.inputDigit('2'));
+    assertEquals("650-253-22", formatter.inputDigit('2'));
+    assertEquals("650-253-222", formatter.inputDigit('2'));
+    assertEquals("650-253-2222", formatter.inputDigit('2'));
 
     formatter.clear();
     assertEquals("0", formatter.inputDigit('0'));
     assertEquals("01", formatter.inputDigit('1'));
     assertEquals("011", formatter.inputDigit('1'));
-    assertEquals("0114", formatter.inputDigit('4'));
+    assertEquals("0114", formatter.inputDigitAndRememberPosition('4'));
     assertEquals("01148", formatter.inputDigit('8'));
+    assertEquals(4, formatter.getRememberedPosition());
     assertEquals("011 48 8", formatter.inputDigit('8'));
+    assertEquals(5, formatter.getRememberedPosition());
     assertEquals("011 48 88", formatter.inputDigit('8'));
     assertEquals("011 48 881", formatter.inputDigit('1'));
     assertEquals("011 48 88 12", formatter.inputDigit('2'));
+    assertEquals(5, formatter.getRememberedPosition());
     assertEquals("011 48 88 123", formatter.inputDigit('3'));
     assertEquals("011 48 88 123 1", formatter.inputDigit('1'));
     assertEquals("011 48 88 123 12", formatter.inputDigit('2'));
@@ -148,15 +222,35 @@
     formatter.clear();
     assertEquals("+", formatter.inputDigit('+'));
     assertEquals("+1", formatter.inputDigit('1'));
-    assertEquals("+16", formatter.inputDigit('6'));
+    assertEquals("+16", formatter.inputDigitAndRememberPosition('6'));
     assertEquals("+165", formatter.inputDigit('5'));
     assertEquals("+1650", formatter.inputDigit('0'));
+    assertEquals(3, formatter.getRememberedPosition());
     assertEquals("+1 650 2", formatter.inputDigit('2'));
+    assertEquals(4, formatter.getRememberedPosition());
+    assertEquals("+1 650 25", formatter.inputDigit('5'));
+    assertEquals("+1 650 253", formatter.inputDigitAndRememberPosition('3'));
+    assertEquals("+1 650 253 2", formatter.inputDigit('2'));
+    assertEquals("+1 650 253 22", formatter.inputDigit('2'));
+    assertEquals("+1 650 253 222", formatter.inputDigit('2'));
+    assertEquals(10, formatter.getRememberedPosition());
+
+    formatter.clear();
+    assertEquals("+", formatter.inputDigit('+'));
+    assertEquals("+1", formatter.inputDigit('1'));
+    assertEquals("+16", formatter.inputDigitAndRememberPosition('6'));
+    assertEquals("+165", formatter.inputDigit('5'));
+    assertEquals("+1650", formatter.inputDigit('0'));
+    assertEquals(3, formatter.getRememberedPosition());
+    assertEquals("+1 650 2", formatter.inputDigit('2'));
+    assertEquals(4, formatter.getRememberedPosition());
     assertEquals("+1 650 25", formatter.inputDigit('5'));
     assertEquals("+1 650 253", formatter.inputDigit('3'));
     assertEquals("+1 650 253 2", formatter.inputDigit('2'));
     assertEquals("+1 650 253 22", formatter.inputDigit('2'));
     assertEquals("+1 650 253 222", formatter.inputDigit('2'));
+    assertEquals("+1650253222;", formatter.inputDigit(';'));
+    assertEquals(3, formatter.getRememberedPosition());
 
     formatter.clear();
     assertEquals("+", formatter.inputDigit('+'));
@@ -214,9 +308,11 @@
     assertEquals("0", formatter.inputDigit('0'));
     assertEquals("02", formatter.inputDigit('2'));
     assertEquals("020", formatter.inputDigit('0'));
-    assertEquals("0207", formatter.inputDigit('7'));
+    assertEquals("0207", formatter.inputDigitAndRememberPosition('7'));
+    assertEquals(4, formatter.getRememberedPosition());
     assertEquals("02070", formatter.inputDigit('0'));
     assertEquals("020 703", formatter.inputDigit('3'));
+    assertEquals(5, formatter.getRememberedPosition());
     assertEquals("020 7031", formatter.inputDigit('1'));
     assertEquals("020 7031 3", formatter.inputDigit('3'));
     assertEquals("020 7031 30", formatter.inputDigit('0'));