• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.utils;
18 
19 import com.android.inputmethod.latin.common.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 int[] sortedSeparators)42     private static final int getStringMode(final String string, final int[] sortedSeparators) {
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, sortedSeparators)) {
48             return CAPS_MODE_FIRST_WORD_UPPER;
49         } else {
50             return CAPS_MODE_ORIGINAL_MIXED_CASE;
51         }
52     }
53 
modeToString(final int recapitalizeMode)54     public static String modeToString(final int recapitalizeMode) {
55         switch (recapitalizeMode) {
56         case NOT_A_RECAPITALIZE_MODE: return "undefined";
57         case CAPS_MODE_ORIGINAL_MIXED_CASE: return "mixedCase";
58         case CAPS_MODE_ALL_LOWER: return "allLower";
59         case CAPS_MODE_FIRST_WORD_UPPER: return "firstWordUpper";
60         case CAPS_MODE_ALL_UPPER: return "allUpper";
61         default: return "unknown<" + recapitalizeMode + ">";
62         }
63     }
64 
65     /**
66      * We store the location of the cursor and the string that was there before the recapitalize
67      * action was done, and the location of the cursor and the string that was there after.
68      */
69     private int mCursorStartBefore;
70     private String mStringBefore;
71     private int mCursorStartAfter;
72     private int mCursorEndAfter;
73     private int mRotationStyleCurrentIndex;
74     private boolean mSkipOriginalMixedCaseMode;
75     private Locale mLocale;
76     private int[] mSortedSeparators;
77     private String mStringAfter;
78     private boolean mIsStarted;
79     private boolean mIsEnabled = true;
80 
81     private static final int[] EMPTY_STORTED_SEPARATORS = {};
82 
RecapitalizeStatus()83     public RecapitalizeStatus() {
84         // By default, initialize with dummy values that won't match any real recapitalize.
85         start(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS);
86         stop();
87     }
88 
start(final int cursorStart, final int cursorEnd, final String string, final Locale locale, final int[] sortedSeparators)89     public void start(final int cursorStart, final int cursorEnd, final String string,
90             final Locale locale, final int[] sortedSeparators) {
91         if (!mIsEnabled) {
92             return;
93         }
94         mCursorStartBefore = cursorStart;
95         mStringBefore = string;
96         mCursorStartAfter = cursorStart;
97         mCursorEndAfter = cursorEnd;
98         mStringAfter = string;
99         final int initialMode = getStringMode(mStringBefore, sortedSeparators);
100         mLocale = locale;
101         mSortedSeparators = sortedSeparators;
102         if (CAPS_MODE_ORIGINAL_MIXED_CASE == initialMode) {
103             mRotationStyleCurrentIndex = 0;
104             mSkipOriginalMixedCaseMode = false;
105         } else {
106             // Find the current mode in the array.
107             int currentMode;
108             for (currentMode = ROTATION_STYLE.length - 1; currentMode > 0; --currentMode) {
109                 if (ROTATION_STYLE[currentMode] == initialMode) {
110                     break;
111                 }
112             }
113             mRotationStyleCurrentIndex = currentMode;
114             mSkipOriginalMixedCaseMode = true;
115         }
116         mIsStarted = true;
117     }
118 
stop()119     public void stop() {
120         mIsStarted = false;
121     }
122 
isStarted()123     public boolean isStarted() {
124         return mIsStarted;
125     }
126 
enable()127     public void enable() {
128         mIsEnabled = true;
129     }
130 
disable()131     public void disable() {
132         mIsEnabled = false;
133     }
134 
mIsEnabled()135     public boolean mIsEnabled() {
136         return mIsEnabled;
137     }
138 
isSetAt(final int cursorStart, final int cursorEnd)139     public boolean isSetAt(final int cursorStart, final int cursorEnd) {
140         return cursorStart == mCursorStartAfter && cursorEnd == mCursorEndAfter;
141     }
142 
143     /**
144      * Rotate through the different possible capitalization modes.
145      */
rotate()146     public void rotate() {
147         final String oldResult = mStringAfter;
148         int count = 0; // Protection against infinite loop.
149         do {
150             mRotationStyleCurrentIndex = (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
151             if (CAPS_MODE_ORIGINAL_MIXED_CASE == ROTATION_STYLE[mRotationStyleCurrentIndex]
152                     && mSkipOriginalMixedCaseMode) {
153                 mRotationStyleCurrentIndex =
154                         (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
155             }
156             ++count;
157             switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) {
158             case CAPS_MODE_ORIGINAL_MIXED_CASE:
159                 mStringAfter = mStringBefore;
160                 break;
161             case CAPS_MODE_ALL_LOWER:
162                 mStringAfter = mStringBefore.toLowerCase(mLocale);
163                 break;
164             case CAPS_MODE_FIRST_WORD_UPPER:
165                 mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSortedSeparators,
166                         mLocale);
167                 break;
168             case CAPS_MODE_ALL_UPPER:
169                 mStringAfter = mStringBefore.toUpperCase(mLocale);
170                 break;
171             default:
172                 mStringAfter = mStringBefore;
173             }
174         } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1);
175         mCursorEndAfter = mCursorStartAfter + mStringAfter.length();
176     }
177 
178     /**
179      * Remove leading/trailing whitespace from the considered string.
180      */
trim()181     public void trim() {
182         final int len = mStringBefore.length();
183         int nonWhitespaceStart = 0;
184         for (; nonWhitespaceStart < len;
185                 nonWhitespaceStart = mStringBefore.offsetByCodePoints(nonWhitespaceStart, 1)) {
186             final int codePoint = mStringBefore.codePointAt(nonWhitespaceStart);
187             if (!Character.isWhitespace(codePoint)) break;
188         }
189         int nonWhitespaceEnd = len;
190         for (; nonWhitespaceEnd > 0;
191                 nonWhitespaceEnd = mStringBefore.offsetByCodePoints(nonWhitespaceEnd, -1)) {
192             final int codePoint = mStringBefore.codePointBefore(nonWhitespaceEnd);
193             if (!Character.isWhitespace(codePoint)) break;
194         }
195         // If nonWhitespaceStart >= nonWhitespaceEnd, that means the selection contained only
196         // whitespace, so we leave it as is.
197         if ((0 != nonWhitespaceStart || len != nonWhitespaceEnd)
198                 && nonWhitespaceStart < nonWhitespaceEnd) {
199             mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd;
200             mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart;
201             mStringAfter = mStringBefore =
202                     mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd);
203         }
204     }
205 
getRecapitalizedString()206     public String getRecapitalizedString() {
207         return mStringAfter;
208     }
209 
getNewCursorStart()210     public int getNewCursorStart() {
211         return mCursorStartAfter;
212     }
213 
getNewCursorEnd()214     public int getNewCursorEnd() {
215         return mCursorEndAfter;
216     }
217 
getCurrentMode()218     public int getCurrentMode() {
219         return ROTATION_STYLE[mRotationStyleCurrentIndex];
220     }
221 }
222