Home | History | Annotate | Download | only in spellcheck
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.inputmethod.latin.spellcheck;
     18 
     19 import android.os.Binder;
     20 import android.text.TextUtils;
     21 import android.util.Log;
     22 import android.view.textservice.SentenceSuggestionsInfo;
     23 import android.view.textservice.SuggestionsInfo;
     24 import android.view.textservice.TextInfo;
     25 
     26 import com.android.inputmethod.latin.CollectionUtils;
     27 
     28 import java.util.ArrayList;
     29 
     30 public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
     31     private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
     32     private static final boolean DBG = false;
     33     private final static String[] EMPTY_STRING_ARRAY = new String[0];
     34 
     35     public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
     36         super(service);
     37     }
     38 
     39     private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
     40             SentenceSuggestionsInfo ssi) {
     41         final String typedText = ti.getText();
     42         if (!typedText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
     43             return null;
     44         }
     45         final int N = ssi.getSuggestionsCount();
     46         final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList();
     47         final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList();
     48         final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
     49                 CollectionUtils.newArrayList();
     50         String currentWord = null;
     51         for (int i = 0; i < N; ++i) {
     52             final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
     53             final int flags = si.getSuggestionsAttributes();
     54             if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
     55                 continue;
     56             }
     57             final int offset = ssi.getOffsetAt(i);
     58             final int length = ssi.getLengthAt(i);
     59             final String subText = typedText.substring(offset, offset + length);
     60             final String prevWord = currentWord;
     61             currentWord = subText;
     62             if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
     63                 continue;
     64             }
     65             final String[] splitTexts =
     66                     subText.split(AndroidSpellCheckerService.SINGLE_QUOTE, -1);
     67             if (splitTexts == null || splitTexts.length <= 1) {
     68                 continue;
     69             }
     70             final int splitNum = splitTexts.length;
     71             for (int j = 0; j < splitNum; ++j) {
     72                 final String splitText = splitTexts[j];
     73                 if (TextUtils.isEmpty(splitText)) {
     74                     continue;
     75                 }
     76                 if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) {
     77                     continue;
     78                 }
     79                 final int newLength = splitText.length();
     80                 // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
     81                 final int newFlags = 0;
     82                 final SuggestionsInfo newSi =
     83                         new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
     84                 newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
     85                 if (DBG) {
     86                     Log.d(TAG, "Override and remove old span over: " + splitText + ", "
     87                             + offset + "," + newLength);
     88                 }
     89                 additionalOffsets.add(offset);
     90                 additionalLengths.add(newLength);
     91                 additionalSuggestionsInfos.add(newSi);
     92             }
     93         }
     94         final int additionalSize = additionalOffsets.size();
     95         if (additionalSize <= 0) {
     96             return null;
     97         }
     98         final int suggestionsSize = N + additionalSize;
     99         final int[] newOffsets = new int[suggestionsSize];
    100         final int[] newLengths = new int[suggestionsSize];
    101         final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
    102         int i;
    103         for (i = 0; i < N; ++i) {
    104             newOffsets[i] = ssi.getOffsetAt(i);
    105             newLengths[i] = ssi.getLengthAt(i);
    106             newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
    107         }
    108         for (; i < suggestionsSize; ++i) {
    109             newOffsets[i] = additionalOffsets.get(i - N);
    110             newLengths[i] = additionalLengths.get(i - N);
    111             newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
    112         }
    113         return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
    114     }
    115 
    116     @Override
    117     public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
    118             int suggestionsLimit) {
    119         final SentenceSuggestionsInfo[] retval =
    120                 super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit);
    121         if (retval == null || retval.length != textInfos.length) {
    122             return retval;
    123         }
    124         for (int i = 0; i < retval.length; ++i) {
    125             final SentenceSuggestionsInfo tempSsi =
    126                     fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
    127             if (tempSsi != null) {
    128                 retval[i] = tempSsi;
    129             }
    130         }
    131         return retval;
    132     }
    133 
    134     @Override
    135     public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
    136             int suggestionsLimit, boolean sequentialWords) {
    137         long ident = Binder.clearCallingIdentity();
    138         try {
    139             final int length = textInfos.length;
    140             final SuggestionsInfo[] retval = new SuggestionsInfo[length];
    141             for (int i = 0; i < length; ++i) {
    142                 final String prevWord;
    143                 if (sequentialWords && i > 0) {
    144                 final String prevWordCandidate = textInfos[i - 1].getText();
    145                 // Note that an empty string would be used to indicate the initial word
    146                 // in the future.
    147                 prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
    148                 } else {
    149                     prevWord = null;
    150                 }
    151                 retval[i] = onGetSuggestionsInternal(textInfos[i], prevWord, suggestionsLimit);
    152                 retval[i].setCookieAndSequence(textInfos[i].getCookie(),
    153                         textInfos[i].getSequence());
    154             }
    155             return retval;
    156         } finally {
    157             Binder.restoreCallingIdentity(ident);
    158         }
    159     }
    160 }
    161