Commits

Anonymous committed 02f3c37

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

  • Participants
  • Parent commits ea02893
  • Branches default

Comments (0)

Files changed (2)

java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java

   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(?=[^,}][^,}])");
 
     prefixBeforeNationalNumber.setLength(0);
     nationalNumber.setLength(0);
     ableToFormat = true;
+    positionRemembered = 0;
+    originalPosition = 0;
     isInternationalFormatting = false;
     if (!currentMetaData.equals(defaultMetaData)) {
       initializeCountrySpecificInfo(defaultCountry);
    */
   public String inputDigit(char nextChar) {
     accruedInput.append(nextChar);
-    // * and # are normally used in mobile codes, which we do not format.
     if (UNSUPPORTED_SYNTAX.matcher(Character.toString(nextChar)).matches()) {
       ableToFormat = false;
     }
     if (!ableToFormat) {
+      if (positionRemembered > 0 && currentOutput.length() > 0) {
+        positionRemembered = originalPosition;
+        currentOutput.setLength(0);
+      }
+      if (rememberPosition) {
+        positionRemembered = accruedInput.length();
+        originalPosition = positionRemembered;
+      }
       return accruedInput.toString();
     }
 
       case 3:
       case 4:
       case 5:
+        if (rememberPosition) {
+          positionRemembered = accruedInput.length();
+          originalPosition = positionRemembered;
+        }
         return accruedInput.toString();
       case 6:
         if (!extractIddAndValidCountryCode()) {
           ableToFormat = false;
+          if (rememberPosition) {
+            positionRemembered = accruedInput.length();
+            originalPosition = positionRemembered;
+          }
           return accruedInput.toString();
         }
         removeNationalPrefixFromNationalNumber();
     }
   }
 
+  /**
+   * Same as inputDigit, but remember the position where nextChar is inserted, so that it could be
+   * retrieved later using getRememberedPosition(). The remembered position will be automatically
+   * adjusted if additional formatting characters are later inserted/removed in front of it.
+   */
+  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() {
       chooseFormatAndCreateTemplate(nationalNumber.substring(0, 4));
       return inputAccruedNationalNumber();
     } else {
+      if (rememberPosition) {
+        positionRemembered = prefixBeforeNationalNumber.length() + nationalNumber.length();
+      }
       return prefixBeforeNationalNumber + nationalNumber.toString();
     }
   }
     int lengthOfNationalNumber = nationalNumber.length();
     if (lengthOfNationalNumber > 0) {
       for (int i = 0; i < lengthOfNationalNumber - 1; i++) {
-        inputDigitHelper(nationalNumber.charAt(i));
+        String temp = inputDigitHelper(nationalNumber.charAt(i));
+        if (positionRemembered == i + 1) {
+          positionRemembered = temp.length();
+        }
       }
-      return prefixBeforeNationalNumber
-             + inputDigitHelper(nationalNumber.charAt(lengthOfNationalNumber - 1));
+      return prefixBeforeNationalNumber +
+          inputDigitHelper(nationalNumber.charAt(lengthOfNationalNumber - 1));
     } else {
+      if (rememberPosition) {
+        positionRemembered = prefixBeforeNationalNumber.length();
+      }
       return prefixBeforeNationalNumber.toString();
     }
   }
         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);
     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;
+      if (rememberPosition) {
+        positionRemembered = prefixBeforeNationalNumber.length() + currentOutput.length();
+      }
       return currentOutput.toString();
     }
   }

java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java

     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());
+
+    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'));
     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('+'));
     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'));