RecapitalizeStatus.java revision 2a81943d162383cc6e9d9429bf3f7949c994a8ba
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
172995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalardpackage com.android.inputmethod.latin;
182995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
192995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalardimport com.android.inputmethod.latin.StringUtils;
202995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
212995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalardimport java.util.Locale;
222995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
232995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard/**
242995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard * The status of the current recapitalize process.
252995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard */
262995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalardpublic class RecapitalizeStatus {
278094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard    public static final int NOT_A_RECAPITALIZE_MODE = -1;
282995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0;
292995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public static final int CAPS_MODE_ALL_LOWER = 1;
302995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public static final int CAPS_MODE_FIRST_WORD_UPPER = 2;
312995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public static final int CAPS_MODE_ALL_UPPER = 3;
322995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    // When adding a new mode, don't forget to update the CAPS_MODE_LAST constant.
332995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public static final int CAPS_MODE_LAST = CAPS_MODE_ALL_UPPER;
342995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
352995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private static final int[] ROTATION_STYLE = {
362995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        CAPS_MODE_ORIGINAL_MIXED_CASE,
372995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        CAPS_MODE_ALL_LOWER,
382995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        CAPS_MODE_FIRST_WORD_UPPER,
392995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        CAPS_MODE_ALL_UPPER
402995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    };
41b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard
422995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private static final int getStringMode(final String string, final String separators) {
432995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        if (StringUtils.isIdenticalAfterUpcase(string)) {
442995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            return CAPS_MODE_ALL_UPPER;
452995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        } else if (StringUtils.isIdenticalAfterDowncase(string)) {
462995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            return CAPS_MODE_ALL_LOWER;
472995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        } else if (StringUtils.isIdenticalAfterCapitalizeEachWord(string, separators)) {
482995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            return CAPS_MODE_FIRST_WORD_UPPER;
492995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        } else {
502995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            return CAPS_MODE_ORIGINAL_MIXED_CASE;
512995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        }
522995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
532995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
542995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    /**
55b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard     * We store the location of the cursor and the string that was there before the recapitalize
562995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard     * action was done, and the location of the cursor and the string that was there after.
572995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard     */
582995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private int mCursorStartBefore;
592995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private String mStringBefore;
602995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private int mCursorStartAfter;
612995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private int mCursorEndAfter;
622995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private int mRotationStyleCurrentIndex;
63b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    private boolean mSkipOriginalMixedCaseMode;
64b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    private Locale mLocale;
65b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    private String mSeparators;
662995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    private String mStringAfter;
67b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    private boolean mIsActive;
68b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard
69b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    public RecapitalizeStatus() {
70b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        // By default, initialize with dummy values that won't match any real recapitalize.
71b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        initialize(-1, -1, "", Locale.getDefault(), "");
72b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        deactivate();
73b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    }
742995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
75b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    public void initialize(final int cursorStart, final int cursorEnd, final String string,
762995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            final Locale locale, final String separators) {
772995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mCursorStartBefore = cursorStart;
782995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mStringBefore = string;
792995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mCursorStartAfter = cursorStart;
802995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mCursorEndAfter = cursorEnd;
812995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mStringAfter = string;
822995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        final int initialMode = getStringMode(mStringBefore, separators);
832995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mLocale = locale;
842995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mSeparators = separators;
852995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        if (CAPS_MODE_ORIGINAL_MIXED_CASE == initialMode) {
862995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mRotationStyleCurrentIndex = 0;
872995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mSkipOriginalMixedCaseMode = false;
882995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        } else {
892995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            // Find the current mode in the array.
902995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            int currentMode;
912995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            for (currentMode = ROTATION_STYLE.length - 1; currentMode > 0; --currentMode) {
922995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                if (ROTATION_STYLE[currentMode] == initialMode) {
932995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                    break;
942995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                }
952995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            }
962995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mRotationStyleCurrentIndex = currentMode;
972995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mSkipOriginalMixedCaseMode = true;
982995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        }
99b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        mIsActive = true;
100b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    }
101b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard
102b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    public void deactivate() {
103b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        mIsActive = false;
104b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    }
105b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard
106b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard    public boolean isActive() {
107b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        return mIsActive;
1082995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1092995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
1102995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public boolean isSetAt(final int cursorStart, final int cursorEnd) {
1112995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        return cursorStart == mCursorStartAfter && cursorEnd == mCursorEndAfter;
1122995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1132995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
1142995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    /**
1152995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard     * Rotate through the different possible capitalization modes.
1162995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard     */
1172995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public void rotate() {
1182995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        final String oldResult = mStringAfter;
1192995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        int count = 0; // Protection against infinite loop.
1202995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        do {
1212995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mRotationStyleCurrentIndex = (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
1222995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            if (CAPS_MODE_ORIGINAL_MIXED_CASE == ROTATION_STYLE[mRotationStyleCurrentIndex]
1232995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                    && mSkipOriginalMixedCaseMode) {
1242995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                mRotationStyleCurrentIndex =
1252995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                        (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
1262995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            }
1272995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            ++count;
1282995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) {
129b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            case CAPS_MODE_ORIGINAL_MIXED_CASE:
130b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                mStringAfter = mStringBefore;
131b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                break;
132b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            case CAPS_MODE_ALL_LOWER:
133b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                mStringAfter = mStringBefore.toLowerCase(mLocale);
134b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                break;
135b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            case CAPS_MODE_FIRST_WORD_UPPER:
136b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators,
137b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                        mLocale);
138b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                break;
139b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            case CAPS_MODE_ALL_UPPER:
140b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                mStringAfter = mStringBefore.toUpperCase(mLocale);
141b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                break;
142b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            default:
143b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard                mStringAfter = mStringBefore;
1442995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            }
145b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard        } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1);
1462995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        mCursorEndAfter = mCursorStartAfter + mStringAfter.length();
1472995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1482995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
1492995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    /**
1502995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard     * Remove leading/trailing whitespace from the considered string.
1512995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard     */
1522995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public void trim() {
1532995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        final int len = mStringBefore.length();
1542995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        int nonWhitespaceStart = 0;
1552995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        for (; nonWhitespaceStart < len;
1562995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                nonWhitespaceStart = mStringBefore.offsetByCodePoints(nonWhitespaceStart, 1)) {
1572995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            final int codePoint = mStringBefore.codePointAt(nonWhitespaceStart);
1582995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            if (!Character.isWhitespace(codePoint)) break;
1592995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        }
1602995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        int nonWhitespaceEnd = len;
1612995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        for (; nonWhitespaceEnd > 0;
1622995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                nonWhitespaceEnd = mStringBefore.offsetByCodePoints(nonWhitespaceEnd, -1)) {
1632995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            final int codePoint = mStringBefore.codePointBefore(nonWhitespaceEnd);
1642995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            if (!Character.isWhitespace(codePoint)) break;
1652995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        }
1662a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard        // If nonWhitespaceStart >= nonWhitespaceEnd, that means the selection contained only
1672a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard        // whitespace, so we leave it as is.
1682a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard        if ((0 != nonWhitespaceStart || len != nonWhitespaceEnd)
1692a81943d162383cc6e9d9429bf3f7949c994a8baJean Chalard                && nonWhitespaceStart < nonWhitespaceEnd) {
170b794e904a3586ac5f2d31fb24d5a1a8f9aa964b8Jean Chalard            mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd;
1712995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart;
1722995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard            mStringAfter = mStringBefore =
1732995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard                    mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd);
1742995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        }
1752995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1762995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
1772995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public String getRecapitalizedString() {
1782995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        return mStringAfter;
1792995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1802995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
1812995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public int getNewCursorStart() {
1822995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        return mCursorStartAfter;
1832995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1842995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard
1852995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    public int getNewCursorEnd() {
1862995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard        return mCursorEndAfter;
1872995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard    }
1888094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard
1898094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard    public int getCurrentMode() {
1908094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard        return ROTATION_STYLE[mRotationStyleCurrentIndex];
1918094bf45d73a5f7257076afc97d91d6708ee03c4Jean Chalard    }
1922995abe7aadd483aa57a9b088740d46ac07bbe46Jean Chalard}
193