15f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi/* 25f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Copyright (C) 2011 The Android Open Source Project 35f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * 45f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Licensed under the Apache License, Version 2.0 (the "License"); 55f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * you may not use this file except in compliance with the License. 65f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * You may obtain a copy of the License at 75f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * 85f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * http://www.apache.org/licenses/LICENSE-2.0 95f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * 105f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Unless required by applicable law or agreed to in writing, software 115f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * distributed under the License is distributed on an "AS IS" BASIS, 125f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * See the License for the specific language governing permissions and 145f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * limitations under the License. 155f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi */ 165f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 175f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagipackage com.android.inputmethod.latin.makedict; 185f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 195f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagiimport com.android.inputmethod.annotations.UsedForTesting; 205f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagiimport com.android.inputmethod.latin.BinaryDictionary; 215f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaokaimport com.android.inputmethod.latin.Dictionary; 22c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagiimport com.android.inputmethod.latin.NgramContext; 23c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagiimport com.android.inputmethod.latin.NgramContext.WordInfo; 244beeb9253a06482299e0c67467531d30436a02fcJean Chalardimport com.android.inputmethod.latin.common.StringUtils; 25b24de426fc98b7550406f54075de4bbbdb2e0ee2Keisuke Kuroyanagiimport com.android.inputmethod.latin.utils.CombinedFormatUtils; 265f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 275f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagiimport java.util.ArrayList; 285f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagiimport java.util.Arrays; 295f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 30d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagiimport javax.annotation.Nullable; 31d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi 325f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi/** 335f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Utility class for a word with a probability. 345f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * 355f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * This is chiefly used to iterate a dictionary. 365f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi */ 375f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagipublic final class WordProperty implements Comparable<WordProperty> { 385f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public final String mWord; 395f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public final ProbabilityInfo mProbabilityInfo; 40c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi public final ArrayList<NgramProperty> mNgrams; 411adca93381d261a6070be2721dbf8b8abafbfe01Keisuke Kuroyanagi // TODO: Support mIsBeginningOfSentence. 421adca93381d261a6070be2721dbf8b8abafbfe01Keisuke Kuroyanagi public final boolean mIsBeginningOfSentence; 435f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public final boolean mIsNotAWord; 4405172bf1a5693c2e108e91436b98ecd35d2dadadAdrian Velicu public final boolean mIsPossiblyOffensive; 45c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi public final boolean mHasNgrams; 465f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 475f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi private int mHashCode = 0; 485f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 49c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi // TODO: Support n-gram. 50aa7abb2d89e559d3e8969b35fe2c75a8793495f1Keisuke Kuroyanagi @UsedForTesting 518ffc631826b108423f98e3ff4d987f067cbc4e0cKeisuke Kuroyanagi public WordProperty(final String word, final ProbabilityInfo probabilityInfo, 52d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi @Nullable final ArrayList<WeightedString> bigrams, 5305172bf1a5693c2e108e91436b98ecd35d2dadadAdrian Velicu final boolean isNotAWord, final boolean isPossiblyOffensive) { 545f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi mWord = word; 558ffc631826b108423f98e3ff4d987f067cbc4e0cKeisuke Kuroyanagi mProbabilityInfo = probabilityInfo; 56b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard if (null == bigrams) { 57b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard mNgrams = null; 58b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard } else { 59b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard mNgrams = new ArrayList<>(); 60b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard final NgramContext ngramContext = new NgramContext(new WordInfo(mWord)); 615f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka for (final WeightedString bigramTarget : bigrams) { 625f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka mNgrams.add(new NgramProperty(bigramTarget, ngramContext)); 63c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi } 64c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi } 651adca93381d261a6070be2721dbf8b8abafbfe01Keisuke Kuroyanagi mIsBeginningOfSentence = false; 665f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi mIsNotAWord = isNotAWord; 6705172bf1a5693c2e108e91436b98ecd35d2dadadAdrian Velicu mIsPossiblyOffensive = isPossiblyOffensive; 68c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi mHasNgrams = bigrams != null && !bigrams.isEmpty(); 695f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 705f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 715f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) { 725f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi return new ProbabilityInfo( 735f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX], 745f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX], 755f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX], 765f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]); 775f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 785f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 795f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi // Construct word property using information from native code. 805f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY. 815f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public WordProperty(final int[] codePoints, final boolean isNotAWord, 8212d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic final boolean isPossiblyOffensive, final boolean hasBigram, 8388fa47a27d45f6460971d0d223aa558e121b3478Keisuke Kuroyanagi final boolean isBeginningOfSentence, final int[] probabilityInfo, 84d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi final ArrayList<int[][]> ngramPrevWordsArray, 85b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray, 8612d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo) { 875f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); 885f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo); 89b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard final ArrayList<NgramProperty> ngrams = new ArrayList<>(); 9088fa47a27d45f6460971d0d223aa558e121b3478Keisuke Kuroyanagi mIsBeginningOfSentence = isBeginningOfSentence; 915f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi mIsNotAWord = isNotAWord; 9205172bf1a5693c2e108e91436b98ecd35d2dadadAdrian Velicu mIsPossiblyOffensive = isPossiblyOffensive; 93c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi mHasNgrams = hasBigram; 94c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi 95d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi final int relatedNgramCount = ngramTargets.size(); 96c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi for (int i = 0; i < relatedNgramCount; i++) { 97c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi final String ngramTargetString = 98d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi StringUtils.getStringFromNullTerminatedCodePointArray(ngramTargets.get(i)); 99c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi final WeightedString ngramTarget = new WeightedString(ngramTargetString, 100d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi createProbabilityInfoFromArray(ngramProbabilityInfo.get(i))); 101b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi final int[][] prevWords = ngramPrevWordsArray.get(i); 102b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi final boolean[] isBeginningOfSentenceArray = 103b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi ngramPrevWordIsBeginningOfSentenceArray.get(i); 104b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi final WordInfo[] wordInfoArray = new WordInfo[prevWords.length]; 105b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi for (int j = 0; j < prevWords.length; j++) { 106b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi wordInfoArray[j] = isBeginningOfSentenceArray[j] 107b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi ? WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO 108b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi : new WordInfo(StringUtils.getStringFromNullTerminatedCodePointArray( 109b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi prevWords[j])); 110b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi } 111b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi final NgramContext ngramContext = new NgramContext(wordInfoArray); 112b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard ngrams.add(new NgramProperty(ngramTarget, ngramContext)); 1135f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 114b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard mNgrams = ngrams.isEmpty() ? null : ngrams; 1155f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1165f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 117c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi // TODO: Remove 118b5ef884fbb6bfd08ce793604cdf7f0941e958a84Keisuke Kuroyanagi @UsedForTesting 119c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi public ArrayList<WeightedString> getBigrams() { 120b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard if (null == mNgrams) { 121b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard return null; 122b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard } 123c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi final ArrayList<WeightedString> bigrams = new ArrayList<>(); 124c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi for (final NgramProperty ngram : mNgrams) { 125c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi if (ngram.mNgramContext.getPrevWordCount() == 1) { 126c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi bigrams.add(ngram.mTargetWord); 127c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi } 128c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi } 129c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi return bigrams; 130c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi } 131c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi 1325f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public int getProbability() { 1335f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi return mProbabilityInfo.mProbability; 1345f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1355f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 1365f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi private static int computeHashCode(WordProperty word) { 1375f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi return Arrays.hashCode(new Object[] { 1385f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi word.mWord, 1395f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi word.mProbabilityInfo, 140c6a6f6a9905ab98516d944ac85933d016e4147fbKeisuke Kuroyanagi word.mNgrams, 1415f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi word.mIsNotAWord, 14205172bf1a5693c2e108e91436b98ecd35d2dadadAdrian Velicu word.mIsPossiblyOffensive 1435f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi }); 1445f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1455f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 1465f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi /** 1475f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Three-way comparison. 1485f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * 1495f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * A Word x is greater than a word y if x has a higher frequency. If they have the same 1505f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * frequency, they are sorted in lexicographic order. 1515f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi */ 1525f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi @Override 1535f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public int compareTo(final WordProperty w) { 1545f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi if (getProbability() < w.getProbability()) return 1; 1555f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi if (getProbability() > w.getProbability()) return -1; 1565f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi return mWord.compareTo(w.mWord); 1575f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1585f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 1595f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi /** 1605f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Equality test. 1615f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * 1625f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * Words are equal if they have the same frequency, the same spellings, and the same 1635f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi * attributes. 1645f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi */ 1655f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi @Override 1665f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public boolean equals(Object o) { 1675f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi if (o == this) return true; 1685f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi if (!(o instanceof WordProperty)) return false; 1695f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi WordProperty w = (WordProperty)o; 17012d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic return mProbabilityInfo.equals(w.mProbabilityInfo) 17112d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic && mWord.equals(w.mWord) && equals(mNgrams, w.mNgrams) 17205172bf1a5693c2e108e91436b98ecd35d2dadadAdrian Velicu && mIsNotAWord == w.mIsNotAWord && mIsPossiblyOffensive == w.mIsPossiblyOffensive 17312d80ebead6a1d7f704a5a3af3b6fe3313ceab05Dan Zivkovic && mHasNgrams == w.mHasNgrams; 1745f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1755f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 176d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi // TDOO: Have a utility method like java.util.Objects.equals. 177d7a51c242bd21aed28b33279add1a2d54cf3bd60Keisuke Kuroyanagi private static <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) { 178b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard if (null == a) { 179b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard return null == b; 180b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard } 181b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard return a.equals(b); 182b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard } 183b28d1cc48794019e63b95b197a6b9cd3fe64275cJean Chalard 1845f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi @Override 1855f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public int hashCode() { 1865f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi if (mHashCode == 0) { 1875f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi mHashCode = computeHashCode(this); 1885f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1895f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi return mHashCode; 1905f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1915f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 1925f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi @UsedForTesting 1935f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public boolean isValid() { 1945f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka return getProbability() != Dictionary.NOT_A_PROBABILITY; 1955f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 1965f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi 1975f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi @Override 1985f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi public String toString() { 199b24de426fc98b7550406f54075de4bbbdb2e0ee2Keisuke Kuroyanagi return CombinedFormatUtils.formatWordProperty(this); 2005f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi } 2015f5feeba13f6f1a907d90365d8037a361d0ff5daKeisuke Kuroyanagi} 202