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