Home | History | Annotate | Download | only in latin
      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 
     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 
     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 
     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 
    102     public void deactivate() {
    103         mIsActive = false;
    104     }
    105 
    106     public boolean isActive() {
    107         return mIsActive;
    108     }
    109 
    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      */
    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      */
    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 
    174     public String getRecapitalizedString() {
    175         return mStringAfter;
    176     }
    177 
    178     public int getNewCursorStart() {
    179         return mCursorStartAfter;
    180     }
    181 
    182     public int getNewCursorEnd() {
    183         return mCursorEndAfter;
    184     }
    185 
    186     public int getCurrentMode() {
    187         return ROTATION_STYLE[mRotationStyleCurrentIndex];
    188     }
    189 }
    190