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 java.util.Locale;
     20 
     21 /**
     22  * The status of the current recapitalize process.
     23  */
     24 public class RecapitalizeStatus {
     25     public static final int NOT_A_RECAPITALIZE_MODE = -1;
     26     public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0;
     27     public static final int CAPS_MODE_ALL_LOWER = 1;
     28     public static final int CAPS_MODE_FIRST_WORD_UPPER = 2;
     29     public static final int CAPS_MODE_ALL_UPPER = 3;
     30     // When adding a new mode, don't forget to update the CAPS_MODE_LAST constant.
     31     public static final int CAPS_MODE_LAST = CAPS_MODE_ALL_UPPER;
     32 
     33     private static final int[] ROTATION_STYLE = {
     34         CAPS_MODE_ORIGINAL_MIXED_CASE,
     35         CAPS_MODE_ALL_LOWER,
     36         CAPS_MODE_FIRST_WORD_UPPER,
     37         CAPS_MODE_ALL_UPPER
     38     };
     39 
     40     private static final int getStringMode(final String string, final int[] sortedSeparators) {
     41         if (StringUtils.isIdenticalAfterUpcase(string)) {
     42             return CAPS_MODE_ALL_UPPER;
     43         } else if (StringUtils.isIdenticalAfterDowncase(string)) {
     44             return CAPS_MODE_ALL_LOWER;
     45         } else if (StringUtils.isIdenticalAfterCapitalizeEachWord(string, sortedSeparators)) {
     46             return CAPS_MODE_FIRST_WORD_UPPER;
     47         } else {
     48             return CAPS_MODE_ORIGINAL_MIXED_CASE;
     49         }
     50     }
     51 
     52     /**
     53      * We store the location of the cursor and the string that was there before the recapitalize
     54      * action was done, and the location of the cursor and the string that was there after.
     55      */
     56     private int mCursorStartBefore;
     57     private String mStringBefore;
     58     private int mCursorStartAfter;
     59     private int mCursorEndAfter;
     60     private int mRotationStyleCurrentIndex;
     61     private boolean mSkipOriginalMixedCaseMode;
     62     private Locale mLocale;
     63     private int[] mSortedSeparators;
     64     private String mStringAfter;
     65     private boolean mIsStarted;
     66     private boolean mIsEnabled = true;
     67 
     68     private static final int[] EMPTY_STORTED_SEPARATORS = {};
     69 
     70     public RecapitalizeStatus() {
     71         // By default, initialize with dummy values that won't match any real recapitalize.
     72         start(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS);
     73         stop();
     74     }
     75 
     76     public void start(final int cursorStart, final int cursorEnd, final String string,
     77             final Locale locale, final int[] sortedSeparators) {
     78         if (!mIsEnabled) {
     79             return;
     80         }
     81         mCursorStartBefore = cursorStart;
     82         mStringBefore = string;
     83         mCursorStartAfter = cursorStart;
     84         mCursorEndAfter = cursorEnd;
     85         mStringAfter = string;
     86         final int initialMode = getStringMode(mStringBefore, sortedSeparators);
     87         mLocale = locale;
     88         mSortedSeparators = sortedSeparators;
     89         if (CAPS_MODE_ORIGINAL_MIXED_CASE == initialMode) {
     90             mRotationStyleCurrentIndex = 0;
     91             mSkipOriginalMixedCaseMode = false;
     92         } else {
     93             // Find the current mode in the array.
     94             int currentMode;
     95             for (currentMode = ROTATION_STYLE.length - 1; currentMode > 0; --currentMode) {
     96                 if (ROTATION_STYLE[currentMode] == initialMode) {
     97                     break;
     98                 }
     99             }
    100             mRotationStyleCurrentIndex = currentMode;
    101             mSkipOriginalMixedCaseMode = true;
    102         }
    103         mIsStarted = true;
    104     }
    105 
    106     public void stop() {
    107         mIsStarted = false;
    108     }
    109 
    110     public boolean isStarted() {
    111         return mIsStarted;
    112     }
    113 
    114     public void enable() {
    115         mIsEnabled = true;
    116     }
    117 
    118     public void disable() {
    119         mIsEnabled = false;
    120     }
    121 
    122     public boolean mIsEnabled() {
    123         return mIsEnabled;
    124     }
    125 
    126     public boolean isSetAt(final int cursorStart, final int cursorEnd) {
    127         return cursorStart == mCursorStartAfter && cursorEnd == mCursorEndAfter;
    128     }
    129 
    130     /**
    131      * Rotate through the different possible capitalization modes.
    132      */
    133     public void rotate() {
    134         final String oldResult = mStringAfter;
    135         int count = 0; // Protection against infinite loop.
    136         do {
    137             mRotationStyleCurrentIndex = (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
    138             if (CAPS_MODE_ORIGINAL_MIXED_CASE == ROTATION_STYLE[mRotationStyleCurrentIndex]
    139                     && mSkipOriginalMixedCaseMode) {
    140                 mRotationStyleCurrentIndex =
    141                         (mRotationStyleCurrentIndex + 1) % ROTATION_STYLE.length;
    142             }
    143             ++count;
    144             switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) {
    145             case CAPS_MODE_ORIGINAL_MIXED_CASE:
    146                 mStringAfter = mStringBefore;
    147                 break;
    148             case CAPS_MODE_ALL_LOWER:
    149                 mStringAfter = mStringBefore.toLowerCase(mLocale);
    150                 break;
    151             case CAPS_MODE_FIRST_WORD_UPPER:
    152                 mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSortedSeparators,
    153                         mLocale);
    154                 break;
    155             case CAPS_MODE_ALL_UPPER:
    156                 mStringAfter = mStringBefore.toUpperCase(mLocale);
    157                 break;
    158             default:
    159                 mStringAfter = mStringBefore;
    160             }
    161         } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1);
    162         mCursorEndAfter = mCursorStartAfter + mStringAfter.length();
    163     }
    164 
    165     /**
    166      * Remove leading/trailing whitespace from the considered string.
    167      */
    168     public void trim() {
    169         final int len = mStringBefore.length();
    170         int nonWhitespaceStart = 0;
    171         for (; nonWhitespaceStart < len;
    172                 nonWhitespaceStart = mStringBefore.offsetByCodePoints(nonWhitespaceStart, 1)) {
    173             final int codePoint = mStringBefore.codePointAt(nonWhitespaceStart);
    174             if (!Character.isWhitespace(codePoint)) break;
    175         }
    176         int nonWhitespaceEnd = len;
    177         for (; nonWhitespaceEnd > 0;
    178                 nonWhitespaceEnd = mStringBefore.offsetByCodePoints(nonWhitespaceEnd, -1)) {
    179             final int codePoint = mStringBefore.codePointBefore(nonWhitespaceEnd);
    180             if (!Character.isWhitespace(codePoint)) break;
    181         }
    182         // If nonWhitespaceStart >= nonWhitespaceEnd, that means the selection contained only
    183         // whitespace, so we leave it as is.
    184         if ((0 != nonWhitespaceStart || len != nonWhitespaceEnd)
    185                 && nonWhitespaceStart < nonWhitespaceEnd) {
    186             mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd;
    187             mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart;
    188             mStringAfter = mStringBefore =
    189                     mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd);
    190         }
    191     }
    192 
    193     public String getRecapitalizedString() {
    194         return mStringAfter;
    195     }
    196 
    197     public int getNewCursorStart() {
    198         return mCursorStartAfter;
    199     }
    200 
    201     public int getNewCursorEnd() {
    202         return mCursorEndAfter;
    203     }
    204 
    205     public int getCurrentMode() {
    206         return ROTATION_STYLE[mRotationStyleCurrentIndex];
    207     }
    208 }
    209