12995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard/* 22995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * Copyright (C) 2013 The Android Open Source Project 32995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * 42995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * Licensed under the Apache License, Version 2.0 (the "License"); you may not 52995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * use this file except in compliance with the License. You may obtain a copy of 62995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * the License at 72995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * 82995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * http://www.apache.org/licenses/LICENSE-2.0 92995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * 102995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * Unless required by applicable law or agreed to in writing, software 112995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 122995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 132995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * License for the specific language governing permissions and limitations under 142995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * the License. 152995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard */ 162995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 17e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasapackage com.android.inputmethod.latin.utils; 182995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 192995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalardimport java.util.Locale; 202995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 212995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard/** 222995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * The status of the current recapitalize process. 232995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard */ 242995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalardpublic class RecapitalizeStatus { 258094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard public static final int NOT_A_RECAPITALIZE_MODE = -1; 262995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0; 272995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public static final int CAPS_MODE_ALL_LOWER = 1; 282995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public static final int CAPS_MODE_FIRST_WORD_UPPER = 2; 292995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public static final int CAPS_MODE_ALL_UPPER = 3; 302995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard // When adding a new mode, don't forget to update the CAPS_MODE_LAST constant. 312995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public static final int CAPS_MODE_LAST = CAPS_MODE_ALL_UPPER; 322995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 332995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private static final int[] ROTATION_STYLE = { 342995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard CAPS_MODE_ORIGINAL_MIXED_CASE, 352995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard CAPS_MODE_ALL_LOWER, 362995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard CAPS_MODE_FIRST_WORD_UPPER, 372995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard CAPS_MODE_ALL_UPPER 382995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard }; 39b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard 402995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private static final int getStringMode(final String string, final String separators) { 412995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard if (StringUtils.isIdenticalAfterUpcase(string)) { 422995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return CAPS_MODE_ALL_UPPER; 432995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } else if (StringUtils.isIdenticalAfterDowncase(string)) { 442995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return CAPS_MODE_ALL_LOWER; 452995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } else if (StringUtils.isIdenticalAfterCapitalizeEachWord(string, separators)) { 462995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return CAPS_MODE_FIRST_WORD_UPPER; 472995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } else { 482995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return CAPS_MODE_ORIGINAL_MIXED_CASE; 492995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 502995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 512995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 522995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard /** 53b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard * We store the location of the cursor and the string that was there before the recapitalize 542995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * action was done, and the location of the cursor and the string that was there after. 552995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard */ 562995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private int mCursorStartBefore; 572995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private String mStringBefore; 582995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private int mCursorStartAfter; 592995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private int mCursorEndAfter; 602995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private int mRotationStyleCurrentIndex; 61b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard private boolean mSkipOriginalMixedCaseMode; 62b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard private Locale mLocale; 63b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard private String mSeparators; 642995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard private String mStringAfter; 65b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard private boolean mIsActive; 66b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard 67b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard public RecapitalizeStatus() { 68b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard // By default, initialize with dummy values that won't match any real recapitalize. 69b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard initialize(-1, -1, "", Locale.getDefault(), ""); 70b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard deactivate(); 71b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard } 722995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 73b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard public void initialize(final int cursorStart, final int cursorEnd, final String string, 742995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard final Locale locale, final String separators) { 752995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mCursorStartBefore = cursorStart; 762995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mStringBefore = string; 772995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mCursorStartAfter = cursorStart; 782995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mCursorEndAfter = cursorEnd; 792995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mStringAfter = string; 802995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard final int initialMode = getStringMode(mStringBefore, separators); 812995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mLocale = locale; 822995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mSeparators = separators; 832995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard if (CAPS_MODE_ORIGINAL_MIXED_CASE == initialMode) { 842995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mRotationStyleCurrentIndex = 0; 852995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mSkipOriginalMixedCaseMode = false; 862995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } else { 872995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard // Find the current mode in the array. 882995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard int currentMode; 892995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard for (currentMode = ROTATION_STYLE.length - 1; currentMode > 0; --currentMode) { 902995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard if (ROTATION_STYLE[currentMode] == initialMode) { 912995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard break; 922995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 932995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 942995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mRotationStyleCurrentIndex = currentMode; 952995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mSkipOriginalMixedCaseMode = true; 962995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 97b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mIsActive = true; 98b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard } 99b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard 100b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard public void deactivate() { 101b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mIsActive = false; 102b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard } 103b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard 104b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard public boolean isActive() { 105b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard return mIsActive; 1062995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1072995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 1082995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public boolean isSetAt(final int cursorStart, final int cursorEnd) { 1092995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return cursorStart == mCursorStartAfter && cursorEnd == mCursorEndAfter; 1102995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1112995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 1122995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard /** 1132995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * Rotate through the different possible capitalization modes. 1142995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard */ 1152995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public void rotate() { 1162995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard final String oldResult = mStringAfter; 1172995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard int count = 0; // Protection against infinite loop. 1182995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard do { 1192995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mRotationStyleCurrentIndex = (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length; 1202995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard if (CAPS_MODE_ORIGINAL_MIXED_CASE == ROTATION_STYLE[mRotationStyleCurrentIndex] 1212995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard && mSkipOriginalMixedCaseMode) { 1222995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mRotationStyleCurrentIndex = 1232995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length; 1242995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1252995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard ++count; 1262995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) { 127b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard case CAPS_MODE_ORIGINAL_MIXED_CASE: 128b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mStringAfter = mStringBefore; 129b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard break; 130b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard case CAPS_MODE_ALL_LOWER: 131b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mStringAfter = mStringBefore.toLowerCase(mLocale); 132b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard break; 133b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard case CAPS_MODE_FIRST_WORD_UPPER: 134b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators, 135b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mLocale); 136b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard break; 137b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard case CAPS_MODE_ALL_UPPER: 138b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mStringAfter = mStringBefore.toUpperCase(mLocale); 139b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard break; 140b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard default: 141b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mStringAfter = mStringBefore; 1422995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 143b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1); 1442995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mCursorEndAfter = mCursorStartAfter + mStringAfter.length(); 1452995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1462995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 1472995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard /** 1482995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * Remove leading/trailing whitespace from the considered string. 1492995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard */ 1502995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public void trim() { 1512995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard final int len = mStringBefore.length(); 1522995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard int nonWhitespaceStart = 0; 1532995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard for (; nonWhitespaceStart < len; 1542995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard nonWhitespaceStart = mStringBefore.offsetByCodePoints(nonWhitespaceStart, 1)) { 1552995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard final int codePoint = mStringBefore.codePointAt(nonWhitespaceStart); 1562995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard if (!Character.isWhitespace(codePoint)) break; 1572995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1582995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard int nonWhitespaceEnd = len; 1592995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard for (; nonWhitespaceEnd > 0; 1602995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard nonWhitespaceEnd = mStringBefore.offsetByCodePoints(nonWhitespaceEnd, -1)) { 1612995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard final int codePoint = mStringBefore.codePointBefore(nonWhitespaceEnd); 1622995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard if (!Character.isWhitespace(codePoint)) break; 1632995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1642a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard // If nonWhitespaceStart >= nonWhitespaceEnd, that means the selection contained only 1652a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard // whitespace, so we leave it as is. 1662a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard if ((0 != nonWhitespaceStart || len != nonWhitespaceEnd) 1672a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard && nonWhitespaceStart < nonWhitespaceEnd) { 168b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd; 1692995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart; 1702995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mStringAfter = mStringBefore = 1712995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd); 1722995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1732995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1742995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 1752995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public String getRecapitalizedString() { 1762995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return mStringAfter; 1772995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1782995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 1792995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public int getNewCursorStart() { 1802995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return mCursorStartAfter; 1812995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1822995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard 1832995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard public int getNewCursorEnd() { 1842995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard return mCursorEndAfter; 1852995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard } 1868094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard 1878094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard public int getCurrentMode() { 1888094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard return ROTATION_STYLE[mRotationStyleCurrentIndex]; 1898094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard } 1902995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard} 191