• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.makedict;
18 
19 import com.android.inputmethod.annotations.UsedForTesting;
20 import com.android.inputmethod.latin.BinaryDictionary;
21 import com.android.inputmethod.latin.Dictionary;
22 import com.android.inputmethod.latin.NgramContext;
23 import com.android.inputmethod.latin.NgramContext.WordInfo;
24 import com.android.inputmethod.latin.common.StringUtils;
25 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
26 
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 
30 import javax.annotation.Nullable;
31 
32 /**
33  * Utility class for a word with a probability.
34  *
35  * This is chiefly used to iterate a dictionary.
36  */
37 public final class WordProperty implements Comparable<WordProperty> {
38     public final String mWord;
39     public final ProbabilityInfo mProbabilityInfo;
40     public final ArrayList<NgramProperty> mNgrams;
41     // TODO: Support mIsBeginningOfSentence.
42     public final boolean mIsBeginningOfSentence;
43     public final boolean mIsNotAWord;
44     public final boolean mIsPossiblyOffensive;
45     public final boolean mHasNgrams;
46 
47     private int mHashCode = 0;
48 
49     // TODO: Support n-gram.
50     @UsedForTesting
WordProperty(final String word, final ProbabilityInfo probabilityInfo, @Nullable final ArrayList<WeightedString> bigrams, final boolean isNotAWord, final boolean isPossiblyOffensive)51     public WordProperty(final String word, final ProbabilityInfo probabilityInfo,
52             @Nullable final ArrayList<WeightedString> bigrams,
53             final boolean isNotAWord, final boolean isPossiblyOffensive) {
54         mWord = word;
55         mProbabilityInfo = probabilityInfo;
56         if (null == bigrams) {
57             mNgrams = null;
58         } else {
59             mNgrams = new ArrayList<>();
60             final NgramContext ngramContext = new NgramContext(new WordInfo(mWord));
61             for (final WeightedString bigramTarget : bigrams) {
62                 mNgrams.add(new NgramProperty(bigramTarget, ngramContext));
63             }
64         }
65         mIsBeginningOfSentence = false;
66         mIsNotAWord = isNotAWord;
67         mIsPossiblyOffensive = isPossiblyOffensive;
68         mHasNgrams = bigrams != null && !bigrams.isEmpty();
69     }
70 
createProbabilityInfoFromArray(final int[] probabilityInfo)71     private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) {
72       return new ProbabilityInfo(
73               probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX],
74               probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX],
75               probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX],
76               probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]);
77     }
78 
79     // Construct word property using information from native code.
80     // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY.
WordProperty(final int[] codePoints, final boolean isNotAWord, final boolean isPossiblyOffensive, final boolean hasBigram, final boolean isBeginningOfSentence, final int[] probabilityInfo, final ArrayList<int[][]> ngramPrevWordsArray, final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray, final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo)81     public WordProperty(final int[] codePoints, final boolean isNotAWord,
82             final boolean isPossiblyOffensive, final boolean hasBigram,
83             final boolean isBeginningOfSentence, final int[] probabilityInfo,
84             final ArrayList<int[][]> ngramPrevWordsArray,
85             final ArrayList<boolean[]> ngramPrevWordIsBeginningOfSentenceArray,
86             final ArrayList<int[]> ngramTargets, final ArrayList<int[]> ngramProbabilityInfo) {
87         mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
88         mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
89         final ArrayList<NgramProperty> ngrams = new ArrayList<>();
90         mIsBeginningOfSentence = isBeginningOfSentence;
91         mIsNotAWord = isNotAWord;
92         mIsPossiblyOffensive = isPossiblyOffensive;
93         mHasNgrams = hasBigram;
94 
95         final int relatedNgramCount = ngramTargets.size();
96         for (int i = 0; i < relatedNgramCount; i++) {
97             final String ngramTargetString =
98                     StringUtils.getStringFromNullTerminatedCodePointArray(ngramTargets.get(i));
99             final WeightedString ngramTarget = new WeightedString(ngramTargetString,
100                     createProbabilityInfoFromArray(ngramProbabilityInfo.get(i)));
101             final int[][] prevWords = ngramPrevWordsArray.get(i);
102             final boolean[] isBeginningOfSentenceArray =
103                     ngramPrevWordIsBeginningOfSentenceArray.get(i);
104             final WordInfo[] wordInfoArray = new WordInfo[prevWords.length];
105             for (int j = 0; j < prevWords.length; j++) {
106                 wordInfoArray[j] = isBeginningOfSentenceArray[j]
107                         ? WordInfo.BEGINNING_OF_SENTENCE_WORD_INFO
108                         : new WordInfo(StringUtils.getStringFromNullTerminatedCodePointArray(
109                                 prevWords[j]));
110             }
111             final NgramContext ngramContext = new NgramContext(wordInfoArray);
112             ngrams.add(new NgramProperty(ngramTarget, ngramContext));
113         }
114         mNgrams = ngrams.isEmpty() ? null : ngrams;
115     }
116 
117     // TODO: Remove
118     @UsedForTesting
getBigrams()119     public ArrayList<WeightedString> getBigrams() {
120         if (null == mNgrams) {
121             return null;
122         }
123         final ArrayList<WeightedString> bigrams = new ArrayList<>();
124         for (final NgramProperty ngram : mNgrams) {
125             if (ngram.mNgramContext.getPrevWordCount() == 1) {
126                 bigrams.add(ngram.mTargetWord);
127             }
128         }
129         return bigrams;
130     }
131 
getProbability()132     public int getProbability() {
133         return mProbabilityInfo.mProbability;
134     }
135 
computeHashCode(WordProperty word)136     private static int computeHashCode(WordProperty word) {
137         return Arrays.hashCode(new Object[] {
138                 word.mWord,
139                 word.mProbabilityInfo,
140                 word.mNgrams,
141                 word.mIsNotAWord,
142                 word.mIsPossiblyOffensive
143         });
144     }
145 
146     /**
147      * Three-way comparison.
148      *
149      * A Word x is greater than a word y if x has a higher frequency. If they have the same
150      * frequency, they are sorted in lexicographic order.
151      */
152     @Override
compareTo(final WordProperty w)153     public int compareTo(final WordProperty w) {
154         if (getProbability() < w.getProbability()) return 1;
155         if (getProbability() > w.getProbability()) return -1;
156         return mWord.compareTo(w.mWord);
157     }
158 
159     /**
160      * Equality test.
161      *
162      * Words are equal if they have the same frequency, the same spellings, and the same
163      * attributes.
164      */
165     @Override
equals(Object o)166     public boolean equals(Object o) {
167         if (o == this) return true;
168         if (!(o instanceof WordProperty)) return false;
169         WordProperty w = (WordProperty)o;
170         return mProbabilityInfo.equals(w.mProbabilityInfo)
171                 && mWord.equals(w.mWord) && equals(mNgrams, w.mNgrams)
172                 && mIsNotAWord == w.mIsNotAWord && mIsPossiblyOffensive == w.mIsPossiblyOffensive
173                 && mHasNgrams == w.mHasNgrams;
174     }
175 
176     // TDOO: Have a utility method like java.util.Objects.equals.
equals(final ArrayList<T> a, final ArrayList<T> b)177     private static <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) {
178         if (null == a) {
179             return null == b;
180         }
181         return a.equals(b);
182     }
183 
184     @Override
hashCode()185     public int hashCode() {
186         if (mHashCode == 0) {
187             mHashCode = computeHashCode(this);
188         }
189         return mHashCode;
190     }
191 
192     @UsedForTesting
isValid()193     public boolean isValid() {
194         return getProbability() != Dictionary.NOT_A_PROBABILITY;
195     }
196 
197     @Override
toString()198     public String toString() {
199         return CombinedFormatUtils.formatWordProperty(this);
200     }
201 }
202