Home | History | Annotate | Download | only in utils
      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 
     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 
     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 
     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 
     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 
    119     public void stop() {
    120         mIsStarted = false;
    121     }
    122 
    123     public boolean isStarted() {
    124         return mIsStarted;
    125     }
    126 
    127     public void enable() {
    128         mIsEnabled = true;
    129     }
    130 
    131     public void disable() {
    132         mIsEnabled = false;
    133     }
    134 
    135     public boolean mIsEnabled() {
    136         return mIsEnabled;
    137     }
    138 
    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      */
    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      */
    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 
    206     public String getRecapitalizedString() {
    207         return mStringAfter;
    208     }
    209 
    210     public int getNewCursorStart() {
    211         return mCursorStartAfter;
    212     }
    213 
    214     public int getNewCursorEnd() {
    215         return mCursorEndAfter;
    216     }
    217 
    218     public int getCurrentMode() {
    219         return ROTATION_STYLE[mRotationStyleCurrentIndex];
    220     }
    221 }
    222