16d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/*
26d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Copyright (C) 2012 The Android Open Source Project
36d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
46d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License");
56d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * you may not use this file except in compliance with the License.
66d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * You may obtain a copy of the License at
76d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
86d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *      http://www.apache.org/licenses/LICENSE-2.0
96d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Unless required by applicable law or agreed to in writing, software
116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS,
126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * See the License for the specific language governing permissions and
146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * limitations under the License.
156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */
166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
176d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpackage android.view;
186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
196d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.res.Configuration;
206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
216d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.text.BreakIterator;
226d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.util.Locale;
236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/**
256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * This class contains the implementation of text segment iterators
266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * for accessibility support.
276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Note: Such iterators are needed in the view package since we want
296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * to be able to iterator over content description of any view.
306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov *
316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide
326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */
336d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpublic final class AccessibilityIterators {
346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    /**
366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     * @hide
376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     */
386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    public static interface TextSegmentIterator {
396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int current);
406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int current);
416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    /**
446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     * @hide
456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov     */
466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected String mText;
496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private final int[] mSegment = new int[2];
516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mText = text;
546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected int[] getRange(int start, int end) {
576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0 || end < 0 || start ==  end) {
586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[0] = start;
616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mSegment[1] = end;
626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return mSegment;
636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
67446079600ece83b22cb91865bcbeb694292b0108Andrii Kulian            implements ViewRootImpl.ConfigChangedCallback {
686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static CharacterTextSegmentIterator sInstance;
696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
70bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private Locale mLocale;
716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected BreakIterator mImpl;
736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
74bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        public static CharacterTextSegmentIterator getInstance(Locale locale) {
756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
76bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                sInstance = new CharacterTextSegmentIterator(locale);
776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
81bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private CharacterTextSegmentIterator(Locale locale) {
82bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            mLocale = locale;
836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            onLocaleChanged(locale);
846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            ViewRootImpl.addConfigCallback(this);
856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public void initialize(String text) {
896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            super.initialize(text);
906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl.setText(text);
916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLegth) {
1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
10239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
1036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
10439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
1056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
10639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!mImpl.isBoundary(start)) {
10739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = mImpl.following(start);
10839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (start == BreakIterator.DONE) {
10939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
11039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                }
1116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int end = mImpl.following(start);
11339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end == BreakIterator.DONE) {
11439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                return null;
11539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
1166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
12839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
12939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLegth) {
13039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLegth;
1316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
13239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!mImpl.isBoundary(end)) {
13339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = mImpl.preceding(end);
13439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (end == BreakIterator.DONE) {
13539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
13639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                }
1376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
13839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int start = mImpl.preceding(end);
13939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start == BreakIterator.DONE) {
1406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
146446079600ece83b22cb91865bcbeb694292b0108Andrii Kulian        public void onConfigurationChanged(Configuration globalConfig) {
147446079600ece83b22cb91865bcbeb694292b0108Andrii Kulian            final Locale locale = globalConfig.getLocales().get(0);
148bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov            if (!mLocale.equals(locale)) {
149bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                mLocale = locale;
1506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                onLocaleChanged(locale);
1516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getCharacterInstance(locale);
1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static WordTextSegmentIterator sInstance;
1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
162bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        public static WordTextSegmentIterator getInstance(Locale locale) {
1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
164bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov                sInstance = new WordTextSegmentIterator(locale);
1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
169bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov        private WordTextSegmentIterator(Locale locale) {
170bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov           super(locale);
1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        protected void onLocaleChanged(Locale locale) {
1756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            mImpl = BreakIterator.getWordInstance(locale);
1766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
1776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
1786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= mText.length()) {
1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
18739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
18939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
19039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
19139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
19239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = mImpl.following(start);
19339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (start == BreakIterator.DONE) {
19439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
1956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
1966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
19739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int end = mImpl.following(start);
19839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end == BreakIterator.DONE || !isEndBoundary(end)) {
1996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLegth = mText.length();
2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLegth <= 0) {
2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
21339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
21439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLegth) {
21539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLegth;
2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
21739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
21839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = mImpl.preceding(end);
21939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                if (end == BreakIterator.DONE) {
22039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    return null;
2216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                }
2226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
22339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            final int start = mImpl.preceding(end);
22439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start == BreakIterator.DONE || !isStartBoundary(start)) {
2256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
23039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isStartBoundary(int index) {
23139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return isLetterOrDigit(index)
23239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == 0 || !isLetterOrDigit(index - 1));
23339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
23439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
23539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isEndBoundary(int index) {
23639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (index > 0 && isLetterOrDigit(index - 1))
23739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == mText.length() || !isLetterOrDigit(index));
23839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
23939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
2406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private boolean isLetterOrDigit(int index) {
2416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (index >= 0 && index < mText.length()) {
2426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                final int codePoint = mText.codePointAt(index);
2436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return Character.isLetterOrDigit(codePoint);
2446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return false;
2466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        private static ParagraphTextSegmentIterator sInstance;
2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public static ParagraphTextSegmentIterator getInstance() {
2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (sInstance == null) {
2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                sInstance = new ParagraphTextSegmentIterator();
2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return sInstance;
2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] following(int offset) {
2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset >= textLength) {
2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
26839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = offset;
2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (start < 0) {
27039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                start = 0;
2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
27239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (start < textLength && mText.charAt(start) == '\n'
27339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                    && !isStartBoundary(start)) {
2749768832da837e7716555d394bd13fb879a223860alanv                start++;
2759768832da837e7716555d394bd13fb879a223860alanv            }
27639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (start >= textLength) {
27739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                return null;
2786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
27939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = start + 1;
28039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (end < textLength && !isEndBoundary(end)) {
2816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                end++;
2826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
2846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
2856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov
2866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        @Override
2876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        public int[] preceding(int offset) {
2886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            final int textLength = mText.length();
2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (textLength <= 0) {
2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (offset <= 0) {
2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
29539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int end = offset;
29639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            if (end > textLength) {
29739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end = textLength;
29839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            }
29939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
30039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                end--;
3016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            if (end <= 0) {
3036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                return null;
3046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
30539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            int start = end - 1;
30639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            while (start > 0 && !isStartBoundary(start)) {
3076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov                start--;
3086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            }
3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov            return getRange(start, end);
3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov        }
31139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
31239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isStartBoundary(int index) {
31339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (mText.charAt(index) != '\n'
31439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == 0 || mText.charAt(index - 1) == '\n'));
31539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
31639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov
31739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        private boolean isEndBoundary(int index) {
31839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov            return (index > 0 && mText.charAt(index - 1) != '\n'
31939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov                && (index == mText.length() || mText.charAt(index) == '\n'));
32039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov        }
3216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov    }
3226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov}
323