18
18
19
19
import java .util .Arrays ;
20
20
import java .util .LinkedHashSet ;
21
+ import java .util .Locale ;
21
22
22
23
class ModifyingSuggester {
24
+ private static final int MAX_CHAR_DISTANCE = 4 ;
23
25
private final LinkedHashSet <String > result = new LinkedHashSet <>();
24
26
private final char [] tryChars ;
25
27
private final SpellChecker speller ;
@@ -30,9 +32,52 @@ class ModifyingSuggester {
30
32
}
31
33
32
34
LinkedHashSet <String > suggest (String word ) {
35
+ tryVariationsOf (word );
36
+
37
+ WordCase wc = WordCase .caseOf (word );
38
+
39
+ if (wc == WordCase .MIXED ) {
40
+ int dot = word .indexOf ('.' );
41
+ if (dot > 0
42
+ && dot < word .length () - 1
43
+ && WordCase .caseOf (word .substring (dot + 1 )) == WordCase .TITLE ) {
44
+ result .add (word .substring (0 , dot + 1 ) + " " + word .substring (dot + 1 ));
45
+ }
46
+
47
+ tryVariationsOf (toLowerCase (word ));
48
+ }
49
+
50
+ return result ;
51
+ }
52
+
53
+ private String toLowerCase (String word ) {
54
+ char [] chars = new char [word .length ()];
55
+ for (int i = 0 ; i < word .length (); i ++) {
56
+ chars [i ] = speller .dictionary .caseFold (word .charAt (i ));
57
+ }
58
+ return new String (chars );
59
+ }
60
+
61
+ private void tryVariationsOf (String word ) {
62
+ trySuggestion (word .toUpperCase (Locale .ROOT ));
63
+ if (checkDictionaryForSplitSuggestions (word )) {
64
+ return ;
65
+ }
66
+
33
67
tryRep (word );
68
+
69
+ trySwappingChars (word );
70
+ tryLongSwap (word );
71
+ tryNeighborKeys (word );
72
+ tryRemovingChar (word );
34
73
tryAddingChar (word );
35
- return result ;
74
+ tryMovingChar (word );
75
+ tryReplacingChar (word );
76
+ tryTwoDuplicateChars (word );
77
+
78
+ if (speller .dictionary .enableSplitSuggestions ) {
79
+ trySplitting (word );
80
+ }
36
81
}
37
82
38
83
private void tryRep (String word ) {
@@ -50,6 +95,75 @@ private void tryRep(String word) {
50
95
}
51
96
}
52
97
98
+ private void trySwappingChars (String word ) {
99
+ int length = word .length ();
100
+ for (int i = 0 ; i < length - 1 ; i ++) {
101
+ char c1 = word .charAt (i );
102
+ char c2 = word .charAt (i + 1 );
103
+ trySuggestion (word .substring (0 , i ) + c2 + c1 + word .substring (i + 2 ));
104
+ }
105
+
106
+ if (length == 4 || length == 5 ) {
107
+ tryDoubleSwapForShortWords (word , length );
108
+ }
109
+ }
110
+
111
+ // ahev -> have, owudl -> would
112
+ private void tryDoubleSwapForShortWords (String word , int length ) {
113
+ char [] candidate = word .toCharArray ();
114
+ candidate [0 ] = word .charAt (1 );
115
+ candidate [1 ] = word .charAt (0 );
116
+ candidate [length - 1 ] = word .charAt (length - 2 );
117
+ candidate [length - 2 ] = word .charAt (length - 1 );
118
+ trySuggestion (new String (candidate ));
119
+
120
+ if (candidate .length == 5 ) {
121
+ candidate [0 ] = word .charAt (0 );
122
+ candidate [1 ] = word .charAt (2 );
123
+ candidate [2 ] = word .charAt (1 );
124
+ trySuggestion (new String (candidate ));
125
+ }
126
+ }
127
+
128
+ private void tryNeighborKeys (String word ) {
129
+ for (int i = 0 ; i < word .length (); i ++) {
130
+ char c = word .charAt (i );
131
+ char up = Character .toUpperCase (c );
132
+ if (up != c ) {
133
+ trySuggestion (word .substring (0 , i ) + up + word .substring (i + 1 ));
134
+ }
135
+
136
+ // check neighbor characters in keyboard string
137
+ for (String group : speller .dictionary .neighborKeyGroups ) {
138
+ if (group .indexOf (c ) >= 0 ) {
139
+ for (int j = 0 ; j < group .length (); j ++) {
140
+ if (group .charAt (j ) != c ) {
141
+ trySuggestion (word .substring (0 , i ) + group .charAt (j ) + word .substring (i + 1 ));
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+
149
+ private void tryLongSwap (String word ) {
150
+ for (int i = 0 ; i < word .length (); i ++) {
151
+ for (int j = i + 2 ; j < word .length () && j <= i + MAX_CHAR_DISTANCE ; j ++) {
152
+ char c1 = word .charAt (i );
153
+ char c2 = word .charAt (j );
154
+ String prefix = word .substring (0 , i );
155
+ String suffix = word .substring (j + 1 );
156
+ trySuggestion (prefix + c2 + word .substring (i + 1 , j ) + c1 + suffix );
157
+ }
158
+ }
159
+ }
160
+
161
+ private void tryRemovingChar (String word ) {
162
+ for (int i = 0 ; i < word .length (); i ++) {
163
+ trySuggestion (word .substring (0 , i ) + word .substring (i + 1 ));
164
+ }
165
+ }
166
+
53
167
private void tryAddingChar (String word ) {
54
168
for (int i = 0 ; i <= word .length (); i ++) {
55
169
String prefix = word .substring (0 , i );
@@ -60,6 +174,75 @@ private void tryAddingChar(String word) {
60
174
}
61
175
}
62
176
177
+ private void tryMovingChar (String word ) {
178
+ for (int i = 0 ; i < word .length (); i ++) {
179
+ for (int j = i + 2 ; j < word .length () && j <= i + MAX_CHAR_DISTANCE ; j ++) {
180
+ String prefix = word .substring (0 , i );
181
+ trySuggestion (prefix + word .substring (i + 1 , j ) + word .charAt (i ) + word .substring (j ));
182
+ trySuggestion (prefix + word .charAt (j ) + word .substring (i , j ) + word .substring (j + 1 ));
183
+ }
184
+ }
185
+ }
186
+
187
+ private void tryReplacingChar (String word ) {
188
+ for (int i = 0 ; i < word .length (); i ++) {
189
+ String prefix = word .substring (0 , i );
190
+ String suffix = word .substring (i + 1 );
191
+ for (char toInsert : tryChars ) {
192
+ if (toInsert != word .charAt (i )) {
193
+ trySuggestion (prefix + toInsert + suffix );
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ // perhaps we doubled two characters
200
+ // (for example vacation -> vacacation)
201
+ private void tryTwoDuplicateChars (String word ) {
202
+ int dupLen = 0 ;
203
+ for (int i = 2 ; i < word .length (); i ++) {
204
+ if (word .charAt (i ) == word .charAt (i - 2 )) {
205
+ dupLen ++;
206
+ if (dupLen == 3 || dupLen == 2 && i >= 4 ) {
207
+ trySuggestion (word .substring (0 , i - 1 ) + word .substring (i + 1 ));
208
+ dupLen = 0 ;
209
+ }
210
+ } else {
211
+ dupLen = 0 ;
212
+ }
213
+ }
214
+ }
215
+
216
+ private boolean checkDictionaryForSplitSuggestions (String word ) {
217
+ boolean found = false ;
218
+ for (int i = 1 ; i < word .length () - 1 ; i ++) {
219
+ String w1 = word .substring (0 , i );
220
+ String w2 = word .substring (i );
221
+ found |= trySuggestion (w1 + " " + w2 );
222
+ if (shouldSplitByDash ()) {
223
+ found |= trySuggestion (w1 + "-" + w2 );
224
+ }
225
+ }
226
+ return found ;
227
+ }
228
+
229
+ private void trySplitting (String word ) {
230
+ for (int i = 1 ; i < word .length () - 1 ; i ++) {
231
+ String w1 = word .substring (0 , i );
232
+ String w2 = word .substring (i );
233
+ if (speller .checkWord (w1 ) && speller .checkWord (w2 )) {
234
+ result .add (w1 + " " + w2 );
235
+ if (shouldSplitByDash ()) {
236
+ result .add (w1 + "-" + w2 );
237
+ }
238
+ }
239
+ }
240
+ }
241
+
242
+ private boolean shouldSplitByDash () {
243
+ return speller .dictionary .tryChars .contains ("-" ) || speller .dictionary .tryChars .contains ("a" );
244
+ }
245
+
63
246
private boolean trySuggestion (String candidate ) {
64
247
if (speller .checkWord (candidate )) {
65
248
result .add (candidate );
0 commit comments