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