1717
1818package android .text .method ;
1919
20- import android .text .CharSequenceIterator ;
21- import android .text .Editable ;
2220import android .text .Selection ;
23- import android .text .Spanned ;
24- import android .text .TextWatcher ;
2521
2622import java .text .BreakIterator ;
27- import java .text .CharacterIterator ;
2823import java .util .Locale ;
2924
3025/**
3631 * {@hide}
3732 */
3833public class WordIterator implements Selection .PositionIterator {
39- private CharSequence mCurrent ;
40- private boolean mCurrentDirty = false ;
34+ // Size of the window for the word iterator, should be greater than the longest word's length
35+ private static final int WINDOW_WIDTH = 50 ;
36+
37+ private String mString ;
38+ private int mOffsetShift ;
4139
4240 private BreakIterator mIterator ;
4341
@@ -56,70 +54,40 @@ public WordIterator(Locale locale) {
5654 mIterator = BreakIterator .getWordInstance (locale );
5755 }
5856
59- private final TextWatcher mWatcher = new TextWatcher () {
60- /** {@inheritDoc} */
61- public void beforeTextChanged (CharSequence s , int start , int count , int after ) {
62- // ignored
63- }
64-
65- /** {@inheritDoc} */
66- public void onTextChanged (CharSequence s , int start , int before , int count ) {
67- mCurrentDirty = true ;
68- }
69-
70- /** {@inheritDoc} */
71- public void afterTextChanged (Editable s ) {
72- // ignored
73- }
74- };
75-
76- public void setCharSequence (CharSequence incoming ) {
77- // When incoming is different object, move listeners to new sequence
78- // and mark as dirty so we reload contents.
79- if (mCurrent != incoming ) {
80- if (mCurrent instanceof Editable ) {
81- ((Editable ) mCurrent ).removeSpan (mWatcher );
82- }
57+ public void setCharSequence (CharSequence charSequence , int start , int end ) {
58+ mOffsetShift = Math .max (0 , start - WINDOW_WIDTH );
59+ final int windowEnd = Math .min (charSequence .length (), end + WINDOW_WIDTH );
8360
84- if (incoming instanceof Editable ) {
85- ((Editable ) incoming ).setSpan (
86- mWatcher , 0 , incoming .length (), Spanned .SPAN_INCLUSIVE_INCLUSIVE );
87- }
88-
89- mCurrent = incoming ;
90- mCurrentDirty = true ;
91- }
92-
93- if (mCurrentDirty ) {
94- final CharacterIterator charIterator = new CharSequenceIterator (mCurrent );
95- mIterator .setText (charIterator );
96-
97- mCurrentDirty = false ;
98- }
61+ mString = charSequence .toString ().substring (mOffsetShift , windowEnd );
62+ mIterator .setText (mString );
9963 }
10064
10165 /** {@inheritDoc} */
10266 public int preceding (int offset ) {
67+ int shiftedOffset = offset - mOffsetShift ;
10368 do {
104- offset = mIterator .preceding (offset );
105- if (offset == BreakIterator .DONE || isOnLetterOrDigit (offset )) {
106- break ;
69+ shiftedOffset = mIterator .preceding (shiftedOffset );
70+ if (shiftedOffset == BreakIterator .DONE ) {
71+ return BreakIterator .DONE ;
72+ }
73+ if (isOnLetterOrDigit (shiftedOffset )) {
74+ return shiftedOffset + mOffsetShift ;
10775 }
10876 } while (true );
109-
110- return offset ;
11177 }
11278
11379 /** {@inheritDoc} */
11480 public int following (int offset ) {
81+ int shiftedOffset = offset - mOffsetShift ;
11582 do {
116- offset = mIterator .following (offset );
117- if (offset == BreakIterator .DONE || isAfterLetterOrDigit (offset )) {
118- break ;
83+ shiftedOffset = mIterator .following (shiftedOffset );
84+ if (shiftedOffset == BreakIterator .DONE ) {
85+ return BreakIterator .DONE ;
86+ }
87+ if (isAfterLetterOrDigit (shiftedOffset )) {
88+ return shiftedOffset + mOffsetShift ;
11989 }
12090 } while (true );
121-
122- return offset ;
12391 }
12492
12593 /** If <code>offset</code> is within a word, returns the index of the first character of that
@@ -135,17 +103,18 @@ public int following(int offset) {
135103 * @throws IllegalArgumentException is offset is not valid.
136104 */
137105 public int getBeginning (int offset ) {
138- checkOffsetIsValid (offset );
106+ final int shiftedOffset = offset - mOffsetShift ;
107+ checkOffsetIsValid (shiftedOffset );
139108
140- if (isOnLetterOrDigit (offset )) {
141- if (mIterator .isBoundary (offset )) {
142- return offset ;
109+ if (isOnLetterOrDigit (shiftedOffset )) {
110+ if (mIterator .isBoundary (shiftedOffset )) {
111+ return shiftedOffset + mOffsetShift ;
143112 } else {
144- return mIterator .preceding (offset ) ;
113+ return mIterator .preceding (shiftedOffset ) + mOffsetShift ;
145114 }
146115 } else {
147- if (isAfterLetterOrDigit (offset )) {
148- return mIterator .preceding (offset ) ;
116+ if (isAfterLetterOrDigit (shiftedOffset )) {
117+ return mIterator .preceding (shiftedOffset ) + mOffsetShift ;
149118 }
150119 }
151120 return BreakIterator .DONE ;
@@ -164,58 +133,44 @@ public int getBeginning(int offset) {
164133 * @throws IllegalArgumentException is offset is not valid.
165134 */
166135 public int getEnd (int offset ) {
167- checkOffsetIsValid (offset );
136+ final int shiftedOffset = offset - mOffsetShift ;
137+ checkOffsetIsValid (shiftedOffset );
168138
169- if (isAfterLetterOrDigit (offset )) {
170- if (mIterator .isBoundary (offset )) {
171- return offset ;
139+ if (isAfterLetterOrDigit (shiftedOffset )) {
140+ if (mIterator .isBoundary (shiftedOffset )) {
141+ return shiftedOffset + mOffsetShift ;
172142 } else {
173- return mIterator .following (offset ) ;
143+ return mIterator .following (shiftedOffset ) + mOffsetShift ;
174144 }
175145 } else {
176- if (isOnLetterOrDigit (offset )) {
177- return mIterator .following (offset ) ;
146+ if (isOnLetterOrDigit (shiftedOffset )) {
147+ return mIterator .following (shiftedOffset ) + mOffsetShift ;
178148 }
179149 }
180150 return BreakIterator .DONE ;
181151 }
182152
183- private boolean isAfterLetterOrDigit (int offset ) {
184- if (offset - 1 >= 0 ) {
185- final char previousChar = mCurrent .charAt (offset - 1 );
186- if (Character .isLetterOrDigit (previousChar )) return true ;
187- if (offset - 2 >= 0 ) {
188- final char previousPreviousChar = mCurrent .charAt (offset - 2 );
189- if (Character .isSurrogatePair (previousPreviousChar , previousChar )) {
190- final int codePoint = Character .toCodePoint (previousPreviousChar , previousChar );
191- return Character .isLetterOrDigit (codePoint );
192- }
193- }
153+ private boolean isAfterLetterOrDigit (int shiftedOffset ) {
154+ if (shiftedOffset >= 1 && shiftedOffset <= mString .length ()) {
155+ final int codePoint = mString .codePointBefore (shiftedOffset );
156+ if (Character .isLetterOrDigit (codePoint )) return true ;
194157 }
195158 return false ;
196159 }
197160
198- private boolean isOnLetterOrDigit (int offset ) {
199- final int length = mCurrent .length ();
200- if (offset < length ) {
201- final char currentChar = mCurrent .charAt (offset );
202- if (Character .isLetterOrDigit (currentChar )) return true ;
203- if (offset + 1 < length ) {
204- final char nextChar = mCurrent .charAt (offset + 1 );
205- if (Character .isSurrogatePair (currentChar , nextChar )) {
206- final int codePoint = Character .toCodePoint (currentChar , nextChar );
207- return Character .isLetterOrDigit (codePoint );
208- }
209- }
161+ private boolean isOnLetterOrDigit (int shiftedOffset ) {
162+ if (shiftedOffset >= 0 && shiftedOffset < mString .length ()) {
163+ final int codePoint = mString .codePointAt (shiftedOffset );
164+ if (Character .isLetterOrDigit (codePoint )) return true ;
210165 }
211166 return false ;
212167 }
213168
214- private void checkOffsetIsValid (int offset ) {
215- if (offset < 0 || offset > mCurrent .length ()) {
216- final String message = "Invalid offset: " + offset +
217- ". Valid range is [0 , " + mCurrent .length () + "]" ;
218- throw new IllegalArgumentException ( message );
169+ private void checkOffsetIsValid (int shiftedOffset ) {
170+ if (shiftedOffset < 0 || shiftedOffset > mString .length ()) {
171+ throw new IllegalArgumentException ( "Invalid offset: " + ( shiftedOffset + mOffsetShift ) +
172+ ". Valid range is [" + mOffsetShift + " , " + ( mString .length () + mOffsetShift ) +
173+ "]" );
219174 }
220175 }
221176}
0 commit comments