Loading java/src/com/android/inputmethod/latin/BinaryDictionary.java +7 −6 Original line number Diff line number Diff line Loading @@ -247,7 +247,9 @@ public final class BinaryDictionary extends Dictionary { final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { if (!isValidDictionary()) return null; if (!isValidDictionary()) { return null; } Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code Loading @@ -257,12 +259,11 @@ public final class BinaryDictionary extends Dictionary { final boolean isGesture = composer.isBatchMode(); final int inputSize; if (!isGesture) { final int composerSize = composer.sizeWithoutTrailingSingleQuotes(); if (composerSize > MAX_WORD_LENGTH - 1) return null; for (int i = 0; i < composerSize; i++) { mInputCodePoints[i] = composer.getCodeAt(i); inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( mInputCodePoints, MAX_WORD_LENGTH); if (inputSize < 0) { return null; } inputSize = composerSize; } else { inputSize = inputPointers.getPointerSize(); } Loading java/src/com/android/inputmethod/latin/WordComposer.java +28 −15 Original line number Diff line number Diff line Loading @@ -131,29 +131,42 @@ public final class WordComposer { return mCodePointSize; } public boolean isSingleLetter() { return size() == 1; /** * Copy the code points in the typed word to a destination array of ints. * * If the array is too small to hold the code points in the typed word, nothing is copied and * -1 is returned. * * @param destination the array of ints. * @param maxSize the size of the array. * @return the number of copied code points. */ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( final int[] destination, final int maxSize) { int i = mTypedWordCache.length() - 1; while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) { --i; } if (i < 0) { // The string is empty or contains only single quotes. return 0; } final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i); if (codePointSize > maxSize) { return -1; } return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0, i + 1, true /* downCase */); } // When the composition contains trailing quotes, we don't pass them to the suggestion engine. // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider // single quotes as separators because of their very common use as apostrophes. public int sizeWithoutTrailingSingleQuotes() { return size() - mTrailingSingleQuotesCount; public boolean isSingleLetter() { return size() == 1; } public final boolean isComposingWord() { return size() > 0; } // TODO: make sure that the index should not exceed MAX_WORD_LENGTH public int getCodeAt(int index) { if (index >= MAX_WORD_LENGTH) { return -1; } return mPrimaryKeyCodes[index]; } public InputPointers getInputPointers() { return mInputPointers; } Loading java/src/com/android/inputmethod/latin/utils/StringUtils.java +31 −2 Original line number Diff line number Diff line Loading @@ -191,13 +191,42 @@ public final class StringUtils { } final int[] codePoints = new int[Character.codePointCount(charSequence, startIndex, endIndex)]; copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex, false /* downCase */); return codePoints; } /** * Copies the codepoints in a CharSequence to an int array. * * This method assumes there is enough space in the array to store the code points. The size * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown. * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while * this method is running, or the behavior is undefined. * This method can optionally downcase code points before copying them, but it pays no attention * to locale while doing so. * * @param destination the int array. * @param charSequence the CharSequence. * @param startIndex the start index inside the string in java chars, inclusive. * @param endIndex the end index inside the string in java chars, exclusive. * @param downCase if this is true, code points will be downcased before being copied. * @return the number of copied code points. */ public static int copyCodePointsAndReturnCodePointCount(final int[] destination, final CharSequence charSequence, final int startIndex, final int endIndex, final boolean downCase) { int destIndex = 0; for (int index = startIndex; index < endIndex; index = Character.offsetByCodePoints(charSequence, index, 1)) { codePoints[destIndex] = Character.codePointAt(charSequence, index); final int codePoint = Character.codePointAt(charSequence, index); // TODO: stop using this, as it's not aware of the locale and does not always do // the right thing. destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint; destIndex++; } return codePoints; return destIndex; } public static int[] toSortedCodePointArray(final String string) { Loading tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java +64 −0 Original line number Diff line number Diff line Loading @@ -308,4 +308,68 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertEquals(objs[i], newObjArray.get(i)); } } public void testToCodePointArray() { final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx"; final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h', 0, 0x2002, 0x2003, 0x3000, 'x', 'x'}; final int[] codePointArray = StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR, 0, STR_WITH_SUPPLEMENTARY_CHAR.length()); assertEquals("toCodePointArray, size matches", codePointArray.length, EXPECTED_RESULT.length); for (int i = 0; i < EXPECTED_RESULT.length; ++i) { assertEquals("toCodePointArray position " + i, codePointArray[i], EXPECTED_RESULT[i]); } } public void testCopyCodePointsAndReturnCodePointCount() { final String STR_WITH_SUPPLEMENTARY_CHAR = "AbcDE\uD861\uDED7fGh\u0000\u2002\u3000あx"; final int[] EXPECTED_RESULT = new int[] { 'A', 'b', 'c', 'D', 'E', 0x286D7, 'f', 'G', 'h', 0, 0x2002, 0x3000, 'あ', 'x'}; final int[] EXPECTED_RESULT_DOWNCASE = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h', 0, 0x2002, 0x3000, 'あ', 'x'}; int[] codePointArray = new int[50]; int codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, STR_WITH_SUPPLEMENTARY_CHAR.length(), false /* downCase */); assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount, EXPECTED_RESULT.length); for (int i = 0; i < codePointCount; ++i) { assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], EXPECTED_RESULT[i]); } codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, STR_WITH_SUPPLEMENTARY_CHAR.length(), true /* downCase */); assertEquals("copyCodePointsAndReturnCodePointCount downcase, size matches", codePointCount, EXPECTED_RESULT_DOWNCASE.length); for (int i = 0; i < codePointCount; ++i) { assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], EXPECTED_RESULT_DOWNCASE[i]); } final int JAVA_CHAR_COUNT = 8; final int CODEPOINT_COUNT = 7; codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */); assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount, CODEPOINT_COUNT); for (int i = 0; i < codePointCount; ++i) { assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], EXPECTED_RESULT[i]); } boolean exceptionHappened = false; codePointArray = new int[5]; try { codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */); } catch (ArrayIndexOutOfBoundsException e) { exceptionHappened = true; } assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small", exceptionHappened); } } Loading
java/src/com/android/inputmethod/latin/BinaryDictionary.java +7 −6 Original line number Diff line number Diff line Loading @@ -247,7 +247,9 @@ public final class BinaryDictionary extends Dictionary { final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { if (!isValidDictionary()) return null; if (!isValidDictionary()) { return null; } Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code Loading @@ -257,12 +259,11 @@ public final class BinaryDictionary extends Dictionary { final boolean isGesture = composer.isBatchMode(); final int inputSize; if (!isGesture) { final int composerSize = composer.sizeWithoutTrailingSingleQuotes(); if (composerSize > MAX_WORD_LENGTH - 1) return null; for (int i = 0; i < composerSize; i++) { mInputCodePoints[i] = composer.getCodeAt(i); inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( mInputCodePoints, MAX_WORD_LENGTH); if (inputSize < 0) { return null; } inputSize = composerSize; } else { inputSize = inputPointers.getPointerSize(); } Loading
java/src/com/android/inputmethod/latin/WordComposer.java +28 −15 Original line number Diff line number Diff line Loading @@ -131,29 +131,42 @@ public final class WordComposer { return mCodePointSize; } public boolean isSingleLetter() { return size() == 1; /** * Copy the code points in the typed word to a destination array of ints. * * If the array is too small to hold the code points in the typed word, nothing is copied and * -1 is returned. * * @param destination the array of ints. * @param maxSize the size of the array. * @return the number of copied code points. */ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( final int[] destination, final int maxSize) { int i = mTypedWordCache.length() - 1; while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) { --i; } if (i < 0) { // The string is empty or contains only single quotes. return 0; } final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i); if (codePointSize > maxSize) { return -1; } return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0, i + 1, true /* downCase */); } // When the composition contains trailing quotes, we don't pass them to the suggestion engine. // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider // single quotes as separators because of their very common use as apostrophes. public int sizeWithoutTrailingSingleQuotes() { return size() - mTrailingSingleQuotesCount; public boolean isSingleLetter() { return size() == 1; } public final boolean isComposingWord() { return size() > 0; } // TODO: make sure that the index should not exceed MAX_WORD_LENGTH public int getCodeAt(int index) { if (index >= MAX_WORD_LENGTH) { return -1; } return mPrimaryKeyCodes[index]; } public InputPointers getInputPointers() { return mInputPointers; } Loading
java/src/com/android/inputmethod/latin/utils/StringUtils.java +31 −2 Original line number Diff line number Diff line Loading @@ -191,13 +191,42 @@ public final class StringUtils { } final int[] codePoints = new int[Character.codePointCount(charSequence, startIndex, endIndex)]; copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex, false /* downCase */); return codePoints; } /** * Copies the codepoints in a CharSequence to an int array. * * This method assumes there is enough space in the array to store the code points. The size * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown. * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while * this method is running, or the behavior is undefined. * This method can optionally downcase code points before copying them, but it pays no attention * to locale while doing so. * * @param destination the int array. * @param charSequence the CharSequence. * @param startIndex the start index inside the string in java chars, inclusive. * @param endIndex the end index inside the string in java chars, exclusive. * @param downCase if this is true, code points will be downcased before being copied. * @return the number of copied code points. */ public static int copyCodePointsAndReturnCodePointCount(final int[] destination, final CharSequence charSequence, final int startIndex, final int endIndex, final boolean downCase) { int destIndex = 0; for (int index = startIndex; index < endIndex; index = Character.offsetByCodePoints(charSequence, index, 1)) { codePoints[destIndex] = Character.codePointAt(charSequence, index); final int codePoint = Character.codePointAt(charSequence, index); // TODO: stop using this, as it's not aware of the locale and does not always do // the right thing. destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint; destIndex++; } return codePoints; return destIndex; } public static int[] toSortedCodePointArray(final String string) { Loading
tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java +64 −0 Original line number Diff line number Diff line Loading @@ -308,4 +308,68 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertEquals(objs[i], newObjArray.get(i)); } } public void testToCodePointArray() { final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx"; final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h', 0, 0x2002, 0x2003, 0x3000, 'x', 'x'}; final int[] codePointArray = StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR, 0, STR_WITH_SUPPLEMENTARY_CHAR.length()); assertEquals("toCodePointArray, size matches", codePointArray.length, EXPECTED_RESULT.length); for (int i = 0; i < EXPECTED_RESULT.length; ++i) { assertEquals("toCodePointArray position " + i, codePointArray[i], EXPECTED_RESULT[i]); } } public void testCopyCodePointsAndReturnCodePointCount() { final String STR_WITH_SUPPLEMENTARY_CHAR = "AbcDE\uD861\uDED7fGh\u0000\u2002\u3000あx"; final int[] EXPECTED_RESULT = new int[] { 'A', 'b', 'c', 'D', 'E', 0x286D7, 'f', 'G', 'h', 0, 0x2002, 0x3000, 'あ', 'x'}; final int[] EXPECTED_RESULT_DOWNCASE = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h', 0, 0x2002, 0x3000, 'あ', 'x'}; int[] codePointArray = new int[50]; int codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, STR_WITH_SUPPLEMENTARY_CHAR.length(), false /* downCase */); assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount, EXPECTED_RESULT.length); for (int i = 0; i < codePointCount; ++i) { assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], EXPECTED_RESULT[i]); } codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, STR_WITH_SUPPLEMENTARY_CHAR.length(), true /* downCase */); assertEquals("copyCodePointsAndReturnCodePointCount downcase, size matches", codePointCount, EXPECTED_RESULT_DOWNCASE.length); for (int i = 0; i < codePointCount; ++i) { assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], EXPECTED_RESULT_DOWNCASE[i]); } final int JAVA_CHAR_COUNT = 8; final int CODEPOINT_COUNT = 7; codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */); assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount, CODEPOINT_COUNT); for (int i = 0; i < codePointCount; ++i) { assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i], EXPECTED_RESULT[i]); } boolean exceptionHappened = false; codePointArray = new int[5]; try { codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray, STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */); } catch (ArrayIndexOutOfBoundsException e) { exceptionHappened = true; } assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small", exceptionHappened); } }