1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.inputmethod.latin; 18 19 import com.android.inputmethod.latin.StringUtils; 20 21 import java.util.Locale; 22 23 /** 24 * The status of the current recapitalize process. 25 */ 26 public class RecapitalizeStatus { 27 public static final int NOT_A_RECAPITALIZE_MODE = -1; 28 public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0; 29 public static final int CAPS_MODE_ALL_LOWER = 1; 30 public static final int CAPS_MODE_FIRST_WORD_UPPER = 2; 31 public static final int CAPS_MODE_ALL_UPPER = 3; 32 // When adding a new mode, don't forget to update the CAPS_MODE_LAST constant. 33 public static final int CAPS_MODE_LAST = CAPS_MODE_ALL_UPPER; 34 35 private static final int[] ROTATION_STYLE = { 36 CAPS_MODE_ORIGINAL_MIXED_CASE, 37 CAPS_MODE_ALL_LOWER, 38 CAPS_MODE_FIRST_WORD_UPPER, 39 CAPS_MODE_ALL_UPPER 40 }; 41 getStringMode(final String string, final String separators)42 private static final int getStringMode(final String string, final String separators) { 43 if (StringUtils.isIdenticalAfterUpcase(string)) { 44 return CAPS_MODE_ALL_UPPER; 45 } else if (StringUtils.isIdenticalAfterDowncase(string)) { 46 return CAPS_MODE_ALL_LOWER; 47 } else if (StringUtils.isIdenticalAfterCapitalizeEachWord(string, separators)) { 48 return CAPS_MODE_FIRST_WORD_UPPER; 49 } else { 50 return CAPS_MODE_ORIGINAL_MIXED_CASE; 51 } 52 } 53 54 /** 55 * We store the location of the cursor and the string that was there before the recapitalize 56 * action was done, and the location of the cursor and the string that was there after. 57 */ 58 private int mCursorStartBefore; 59 private String mStringBefore; 60 private int mCursorStartAfter; 61 private int mCursorEndAfter; 62 private int mRotationStyleCurrentIndex; 63 private boolean mSkipOriginalMixedCaseMode; 64 private Locale mLocale; 65 private String mSeparators; 66 private String mStringAfter; 67 private boolean mIsActive; 68 RecapitalizeStatus()69 public RecapitalizeStatus() { 70 // By default, initialize with dummy values that won't match any real recapitalize. 71 initialize(-1, -1, "", Locale.getDefault(), ""); 72 deactivate(); 73 } 74 initialize(final int cursorStart, final int cursorEnd, final String string, final Locale locale, final String separators)75 public void initialize(final int cursorStart, final int cursorEnd, final String string, 76 final Locale locale, final String separators) { 77 mCursorStartBefore = cursorStart; 78 mStringBefore = string; 79 mCursorStartAfter = cursorStart; 80 mCursorEndAfter = cursorEnd; 81 mStringAfter = string; 82 final int initialMode = getStringMode(mStringBefore, separators); 83 mLocale = locale; 84 mSeparators = separators; 85 if (CAPS_MODE_ORIGINAL_MIXED_CASE == initialMode) { 86 mRotationStyleCurrentIndex = 0; 87 mSkipOriginalMixedCaseMode = false; 88 } else { 89 // Find the current mode in the array. 90 int currentMode; 91 for (currentMode = ROTATION_STYLE.length - 1; currentMode > 0; --currentMode) { 92 if (ROTATION_STYLE[currentMode] == initialMode) { 93 break; 94 } 95 } 96 mRotationStyleCurrentIndex = currentMode; 97 mSkipOriginalMixedCaseMode = true; 98 } 99 mIsActive = true; 100 } 101 deactivate()102 public void deactivate() { 103 mIsActive = false; 104 } 105 isActive()106 public boolean isActive() { 107 return mIsActive; 108 } 109 isSetAt(final int cursorStart, final int cursorEnd)110 public boolean isSetAt(final int cursorStart, final int cursorEnd) { 111 return cursorStart == mCursorStartAfter && cursorEnd == mCursorEndAfter; 112 } 113 114 /** 115 * Rotate through the different possible capitalization modes. 116 */ rotate()117 public void rotate() { 118 final String oldResult = mStringAfter; 119 int count = 0; // Protection against infinite loop. 120 do { 121 mRotationStyleCurrentIndex = (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length; 122 if (CAPS_MODE_ORIGINAL_MIXED_CASE == ROTATION_STYLE[mRotationStyleCurrentIndex] 123 && mSkipOriginalMixedCaseMode) { 124 mRotationStyleCurrentIndex = 125 (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length; 126 } 127 ++count; 128 switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) { 129 case CAPS_MODE_ORIGINAL_MIXED_CASE: 130 mStringAfter = mStringBefore; 131 break; 132 case CAPS_MODE_ALL_LOWER: 133 mStringAfter = mStringBefore.toLowerCase(mLocale); 134 break; 135 case CAPS_MODE_FIRST_WORD_UPPER: 136 mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators, 137 mLocale); 138 break; 139 case CAPS_MODE_ALL_UPPER: 140 mStringAfter = mStringBefore.toUpperCase(mLocale); 141 break; 142 default: 143 mStringAfter = mStringBefore; 144 } 145 } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1); 146 mCursorEndAfter = mCursorStartAfter + mStringAfter.length(); 147 } 148 149 /** 150 * Remove leading/trailing whitespace from the considered string. 151 */ trim()152 public void trim() { 153 final int len = mStringBefore.length(); 154 int nonWhitespaceStart = 0; 155 for (; nonWhitespaceStart < len; 156 nonWhitespaceStart = mStringBefore.offsetByCodePoints(nonWhitespaceStart, 1)) { 157 final int codePoint = mStringBefore.codePointAt(nonWhitespaceStart); 158 if (!Character.isWhitespace(codePoint)) break; 159 } 160 int nonWhitespaceEnd = len; 161 for (; nonWhitespaceEnd > 0; 162 nonWhitespaceEnd = mStringBefore.offsetByCodePoints(nonWhitespaceEnd, -1)) { 163 final int codePoint = mStringBefore.codePointBefore(nonWhitespaceEnd); 164 if (!Character.isWhitespace(codePoint)) break; 165 } 166 if (0 != nonWhitespaceStart || len != nonWhitespaceEnd) { 167 mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd; 168 mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart; 169 mStringAfter = mStringBefore = 170 mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd); 171 } 172 } 173 getRecapitalizedString()174 public String getRecapitalizedString() { 175 return mStringAfter; 176 } 177 getNewCursorStart()178 public int getNewCursorStart() { 179 return mCursorStartAfter; 180 } 181 getNewCursorEnd()182 public int getNewCursorEnd() { 183 return mCursorEndAfter; 184 } 185 getCurrentMode()186 public int getCurrentMode() { 187 return ROTATION_STYLE[mRotationStyleCurrentIndex]; 188 } 189 } 190