NgramContext.java revision 11a3965f8c376db4d8fbdf3c6ea6ac54550ae6ed
1a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian/* 2a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * Copyright (C) 2014 The Android Open Source Project 3a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * 4a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * Licensed under the Apache License, Version 2.0 (the "License"); 5a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * you may not use this file except in compliance with the License. 6a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * You may obtain a copy of the License at 7a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * 8a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * http://www.apache.org/licenses/LICENSE-2.0 9a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * 10a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * Unless required by applicable law or agreed to in writing, software 11a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * distributed under the License is distributed on an "AS IS" BASIS, 12a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * See the License for the specific language governing permissions and 14a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * limitations under the License. 15a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian */ 16a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 172965b26022f95051f65b09d7eac47cbe923855c9Mathias Agopianpackage com.android.inputmethod.latin; 182965b26022f95051f65b09d7eac47cbe923855c9Mathias Agopian 19a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopianimport android.text.TextUtils; 20f1352df47fe20aed23c216a78923c7d248f2bb91Mathias Agopian 21f1352df47fe20aed23c216a78923c7d248f2bb91Mathias Agopianimport com.android.inputmethod.annotations.UsedForTesting; 22f1352df47fe20aed23c216a78923c7d248f2bb91Mathias Agopianimport com.android.inputmethod.latin.common.Constants; 23a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopianimport com.android.inputmethod.latin.common.StringUtils; 24a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 25a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopianimport java.util.Arrays; 268372785879d329f592f6883620b5a32d80d74691Mathias Agopian 273eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopianimport javax.annotation.Nonnull; 282965b26022f95051f65b09d7eac47cbe923855c9Mathias Agopian 2922da60c3e64cd57535cbba063c07127814a2b52fMathias Agopian/** 30a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * Class to represent information of previous words. This class is used to add n-gram entries 31a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * into binary dictionaries, to get predictions, and to get suggestions. 323eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian */ 33a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopianpublic class NgramContext { 34a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian @Nonnull 35a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian public static final NgramContext EMPTY_PREV_WORDS_INFO = 36a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian new NgramContext(WordInfo.EMPTY_WORD_INFO); 37a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian @Nonnull 3822da60c3e64cd57535cbba063c07127814a2b52fMathias Agopian public static final NgramContext BEGINNING_OF_SENTENCE = 39a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian new NgramContext(WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO); 40c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian 41a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian /** 42a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian * Word information used to represent previous words information. 43a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian */ 44a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian public static class WordInfo { 453eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian @Nonnull 463eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian public static final WordInfo EMPTY_WORD_INFO = new WordInfo(null); 473eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian @Nonnull 483eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian public static final WordInfo BEGINNING_OF_SENTENCE_WORD_INFO = new WordInfo(); 49c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian 50c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian // This is an empty char sequence when mIsBeginningOfSentence is true. 519c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian public final CharSequence mWord; 523eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian // TODO: Have sentence separator. 533eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian // Whether the current context is beginning of sentence or not. This is true when composing 542965b26022f95051f65b09d7eac47cbe923855c9Mathias Agopian // at the beginning of an input field or composing a word after a sentence separator. 55a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian public final boolean mIsBeginningOfSentence; 563a77871383bc1a03cc866686d81628493d14de7cMathias Agopian 57a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // Beginning of sentence. 5832397c1cd3327905173b36baa6fd1c579bc328ffSteve Block private WordInfo() { 59a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian mWord = ""; 60a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian mIsBeginningOfSentence = true; 61e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block } 62a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 63c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian public WordInfo(final CharSequence word) { 64c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian mWord = word; 65c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian mIsBeginningOfSentence = false; 66c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian } 6731d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian 68c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian public boolean isValid() { 6931d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian return mWord != null; 70c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian } 713eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian 723a77871383bc1a03cc866686d81628493d14de7cMathias Agopian @Override 733eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian public int hashCode() { 74c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian return Arrays.hashCode(new Object[] { mWord, mIsBeginningOfSentence } ); 753a77871383bc1a03cc866686d81628493d14de7cMathias Agopian } 763a77871383bc1a03cc866686d81628493d14de7cMathias Agopian 773a77871383bc1a03cc866686d81628493d14de7cMathias Agopian @Override 783a77871383bc1a03cc866686d81628493d14de7cMathias Agopian public boolean equals(Object o) { 793a77871383bc1a03cc866686d81628493d14de7cMathias Agopian if (this == o) return true; 803a77871383bc1a03cc866686d81628493d14de7cMathias Agopian if (!(o instanceof WordInfo)) return false; 813a77871383bc1a03cc866686d81628493d14de7cMathias Agopian final WordInfo wordInfo = (WordInfo)o; 82a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian if (mWord == null || wordInfo.mWord == null) { 83a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian return mWord == wordInfo.mWord 84a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian && mIsBeginningOfSentence == wordInfo.mIsBeginningOfSentence; 85a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian } 86a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian return TextUtils.equals(mWord, wordInfo.mWord) 873eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian && mIsBeginningOfSentence == wordInfo.mIsBeginningOfSentence; 883eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian } 893eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian } 90a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 91a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // The words immediately before the considered word. EMPTY_WORD_INFO element means we don't 92a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // have any context for that previous word including the "beginning of sentence context" - we 93a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // just don't know what to predict using the information. An example of that is after a comma. 94a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // For simplicity of implementation, elements may also be EMPTY_WORD_INFO transiently after the 95a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // WordComposer was reset and before starting a new composing word, but we should never be 96a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // calling getSuggetions* in this situation. 97a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian private final WordInfo[] mPrevWordsInfo; 98a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian private final int mPrevWordsCount; 99c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian 100c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian // Construct from the previous word information. 101c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian public NgramContext(final WordInfo... prevWordsInfo) { 102c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian mPrevWordsInfo = prevWordsInfo; 10331d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian mPrevWordsCount = prevWordsInfo.length; 10431d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian } 10531d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian 10631d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian // Create next prevWordsInfo using current prevWordsInfo. 107c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian @Nonnull 108e2c2f9213f936f98db604dc9c126ff22f725a824Mathias Agopian public NgramContext getNextNgramContext(final WordInfo wordInfo) { 109c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian final int nextPrevWordCount = Math.min(Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM, 110c7d14e247117392fbd44aa454622778a25c076aeMathias Agopian mPrevWordsCount + 1); 11131d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian final WordInfo[] prevWordsInfo = new WordInfo[nextPrevWordCount]; 1122965b26022f95051f65b09d7eac47cbe923855c9Mathias Agopian prevWordsInfo[0] = wordInfo; 1133eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian System.arraycopy(mPrevWordsInfo, 0, prevWordsInfo, 1, nextPrevWordCount - 1); 1143eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian return new NgramContext(prevWordsInfo); 1153eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian } 1163eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian 1173eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian public boolean isValid() { 1181a3bf41b7165ba294af46bc32483eaad61e707eaErik Gilling return mPrevWordsCount > 0 && mPrevWordsInfo[0].isValid(); 1193eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian } 1203eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian 1213a77871383bc1a03cc866686d81628493d14de7cMathias Agopian public boolean isBeginningOfSentenceContext() { 1223a77871383bc1a03cc866686d81628493d14de7cMathias Agopian return mPrevWordsCount > 0 && mPrevWordsInfo[0].mIsBeginningOfSentence; 1233a77871383bc1a03cc866686d81628493d14de7cMathias Agopian } 1243a77871383bc1a03cc866686d81628493d14de7cMathias Agopian 1253a77871383bc1a03cc866686d81628493d14de7cMathias Agopian // n is 1-indexed. 1263eb38cb33e41ce40dd1094bdec850f0fca9f8a53Mathias Agopian // TODO: Remove 12731d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian public CharSequence getNthPrevWord(final int n) { 12831d2843b45ebdb69ec3355111b7567363fd2a6b7Mathias Agopian if (n <= 0 || n > mPrevWordsCount) { 129a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian return null; 130a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian } 131a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian return mPrevWordsInfo[n - 1].mWord; 132a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian } 133a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 134a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian // n is 1-indexed. 13545721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian @UsedForTesting 13645721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian public boolean isNthPrevWordBeginningOfSentence(final int n) { 13745721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian if (n <= 0 || n > mPrevWordsCount) { 13845721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian return false; 13945721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian } 14045721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian return mPrevWordsInfo[n - 1].mIsBeginningOfSentence; 14145721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian } 142a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 143a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian public void outputToArray(final int[][] codePointArrays, 144a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian final boolean[] isBeginningOfSentenceArray) { 145a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian for (int i = 0; i < mPrevWordsCount; i++) { 146a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian final WordInfo wordInfo = mPrevWordsInfo[i]; 147a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian if (wordInfo == null || !wordInfo.isValid()) { 148a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian codePointArrays[i] = new int[0]; 149a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian isBeginningOfSentenceArray[i] = false; 1509c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian continue; 1519c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 1529c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian codePointArrays[i] = StringUtils.toCodePointArray(wordInfo.mWord); 1539c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian isBeginningOfSentenceArray[i] = wordInfo.mIsBeginningOfSentence; 1549c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 1559c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 1569c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian 1579c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian public int getPrevWordCount() { 1589c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian return mPrevWordsCount; 1599c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 1609c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian 1619c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian @Override 1629c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian public int hashCode() { 1639c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian int hashValue = 0; 1649c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian for (final WordInfo wordInfo : mPrevWordsInfo) { 1659c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian if (wordInfo == null || !WordInfo.EMPTY_WORD_INFO.equals(wordInfo)) { 1669c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian break; 1679c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 1689c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian hashValue ^= wordInfo.hashCode(); 1699c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 1709c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian return hashValue; 171a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian } 172a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian 173a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian @Override 1749c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian public boolean equals(Object o) { 1759c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian if (this == o) return true; 1769c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian if (!(o instanceof NgramContext)) return false; 1779c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian final NgramContext prevWordsInfo = (NgramContext)o; 1789c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian 1799c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian final int minLength = Math.min(mPrevWordsCount, prevWordsInfo.mPrevWordsCount); 1809c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian for (int i = 0; i < minLength; i++) { 1819c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian if (!mPrevWordsInfo[i].equals(prevWordsInfo.mPrevWordsInfo[i])) { 1829c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian return false; 1839c6e297271ec9af9d974242d89cfa08cb6ceaa0aMathias Agopian } 184a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian } 185a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian final WordInfo[] longerWordsInfo; 18658959343dbdb6157fa5f5463262d4842b8954353Mathias Agopian final int longerWordsInfoCount; 18758959343dbdb6157fa5f5463262d4842b8954353Mathias Agopian if (mPrevWordsCount > prevWordsInfo.mPrevWordsCount) { 18858959343dbdb6157fa5f5463262d4842b8954353Mathias Agopian longerWordsInfo = mPrevWordsInfo; 189a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian longerWordsInfoCount = mPrevWordsCount; 190a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian } else { 191a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian longerWordsInfo = prevWordsInfo.mPrevWordsInfo; 192f5f2712854599b4970643c6000fe6ae950a08ba9Antti Hatala longerWordsInfoCount = prevWordsInfo.mPrevWordsCount; 1937ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian } 1947ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian for (int i = minLength; i < longerWordsInfoCount; i++) { 1957ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian if (longerWordsInfo[i] != null 1967ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian && !WordInfo.EMPTY_WORD_INFO.equals(longerWordsInfo[i])) { 1977ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian return false; 1987ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian } 1997ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian } 2007ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian return true; 2017ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian } 2027ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian 2037ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian @Override 2047ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian public String toString() { 2057ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian final StringBuffer builder = new StringBuffer(); 2067ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian for (int i = 0; i < mPrevWordsCount; i++) { 2077ee4cd5556cef1878e1d4729f1b389f186311027Mathias Agopian final WordInfo wordInfo = mPrevWordsInfo[i]; 208f5f2712854599b4970643c6000fe6ae950a08ba9Antti Hatala builder.append("PrevWord["); 209f5f2712854599b4970643c6000fe6ae950a08ba9Antti Hatala builder.append(i); 21045721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian builder.append("]: "); 21145721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian if (wordInfo == null) { 212a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian builder.append("null. "); 213a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian continue; 21445721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian } 21545721773e1a68e96da4b6cc04cef276bae7ca3e9Mathias Agopian if (!wordInfo.isValid()) { 216a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian builder.append("Empty. "); 217a350ff98692b3a50cad5cc93f9f83221242ca86aMathias Agopian continue; 21822da60c3e64cd57535cbba063c07127814a2b52fMathias Agopian } 21922da60c3e64cd57535cbba063c07127814a2b52fMathias Agopian builder.append(wordInfo.mWord); 2208372785879d329f592f6883620b5a32d80d74691Mathias Agopian builder.append(", isBeginningOfSentence: "); 2218372785879d329f592f6883620b5a32d80d74691Mathias Agopian builder.append(wordInfo.mIsBeginningOfSentence); 2228372785879d329f592f6883620b5a32d80d74691Mathias Agopian builder.append(". "); 2238372785879d329f592f6883620b5a32d80d74691Mathias Agopian } 2248372785879d329f592f6883620b5a32d80d74691Mathias Agopian return builder.toString(); 2258372785879d329f592f6883620b5a32d80d74691Mathias Agopian } 226fb4d5d5726c172adbe62341d99a2148685a98379Mathias Agopian} 227aebac5f34a098f733f887c993bd617a393e10db8Mathias Agopian